穿针引线以此类推ListNode*reverseBetween(ListNode*head,intleft,intright){ListNode*dummyNode=newListNode(-1);dummyNode->next=head;ListNode*pre=dummyNode;for(inti=0;i<left-1;++i){pre=pre->next;}ListNode*cur=pre->next;ListNode*next;for(inti=0;i<right-left;++i){next=cur->next;cur->next=next->next;//从这开始next->next=pre->next;pre->next=next;}returndummyNode->next;}
一、tcp和udp的区别,如何设计可靠udp传输转载自:https://www.cnblogs.com/williamjie/p/11133180.htmlUDP不属于连接协议,具有资源消耗少,处理速度快的优点,所以通常音频,视频和普通数据在传送时,使用UDP较多,因为即使丢失少量的包,也不会对接受结果产生较大的影响。传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。最简单的方式是在应用层模仿传输层TCP的可靠性传输。下面不考虑拥塞处理,可靠UDP的简单设计。1、添加seq/ack机制,确保数据发送到对端2、添加发送和接收缓冲区,主要是用户超时重传。3、添加超时重传机制。详细说明:送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。时间到后,定时任务检查是否需要重传数据。目前有如下开源程序利用udp实现了可靠的数据传输。分别为RUDP、RTP、UDT。二、RSA算法设计三、https过程HTTP的请求过程:1、TCP建立连接后,客户端会发送报文给服务端;2、服务端接收报文并作出响应;3、客户端收到响应后解析给用户;HTTPS的请求过程:1、客户端发送请求到服务端;2、服务器返回证书和公钥;3、客户端验证证书和公钥的有效性,如果有效,则生成对称密钥并使用公钥加密发送到服务端;4、服务端使用私钥解密报文,并使用收到的对称密钥加密报文,发送到客户端;5、客户端使用对称密钥解密报文;6、SSL加密建立四、tendermint共识五、以太坊EVM是否熟悉
又tm一天cfunclongestPalindrome(sstring)string{varn=len(s)vardp=make([][]bool,n)varmax=1varstartintfori:=0;i<n;i++{dp[i]=make([]bool,n)}forj:=0;j<n;j++{fori:=0;i<=j;i++{ifj-i<2{dp[i][j]=(s[i]==s[j])}else{dp[i][j]=(dp[i+1][j-1]&&s[i]==s[j])}ifdp[i][j]&&j-i+1>max{max=j-i+1start=i}}}returns[start:start+max]}注意到两个循环,必须要在保证判断dp[i][j]时,dp[i+1][j+1]里这个区间所有的元素都已经正确赋值,一开始写的循环“aaaaa”,一直报错,就是因为判断s[4]的时候,dp[0][3]虽然true了,但是dp[1][4]还是false,还有个问题就是之前的循环一个元素和两个字符比如“aa”这样的回文子串判断不出来,原因也是遍历不到位的原因,dp[i+1][j-1]还是原来那个元素,并没有正确赋值。
mb,错了一天,D了一天bug,真爽。脑子秀逗的感觉。funclengthOfLongestSubstring(sstring)int{varstart,end=0,0varmax=0varhashMap=make(map[byte]int,100)//hashMap[s[0]]=0//t,ok:=hashMap[s[end+1]]for;end<len(s);{t,ok:=hashMap[s[end]]ifok&&t>=start{//这里之前没有判断t,导致在map中,start之前位置可能有重复字符,导致失败。ifmax<end-start{max=end-start}start=hashMap[s[end]]+1}else{hashMap[s[end]]=endend++}}ifmax<end-start{//如果整个字符串一个重复字符没有,那么end=lenl(s)直接退出,不会执行循环里的长度判断max=end-start}returnmax}又写了一个切片的funclengthOfLongestSubstring(sstring)int{varmax=0varlocations=[128]int{0}fori:=0;i<len(s);i++{//切片下标为字母,所以一开始没设置为-1时,下面读的locations可能为0,等于了start,但实际第一个元素还没有存入locations[s[i]]=-1}n:=len(s)varstart,end=0,0for;end<len(s);end++{iflocations[s[end]]>=start{start=locations[s[end]]+1}ifend-start+1>max{max=end-start+1ifmax>=n-start{break}}locations[s[end]]=end}returnmax}
短路求值:作为“&&”和“||”操作数表达式,这些表达式在进行求值时,只要最终的结果已经确定真或假,求值过程便告终,这就是短路求值。因此可以利用左边的表达式来作为递归结束的判断条件。因此递归的表达式就在右边了。使用&&,表示两边都为真,才为真,左边为假,右边就没用了。因此在不断递归时,直到左边为假时,才不执行右边。因此在第一次进行右边的判断时,就进入递归的调用。funcSum_Solution(nint)int{varffunc(ret*int,nint)boolf=func(ret*int,nint)bool{*ret+=nreturnn>0&&f(ret,n-1)}varsumintf(&sum,n)returnsum}
占用空间太大funcreConstructBinaryTree(pre[]int,vin[]int)*TreeNode{//writecodehereiflen(pre)==0{returnnil}varleft_pre,right_pre,left_vin,right_vin[]intvarroot=&TreeNode{pre[0],nil,nil}varroot_son=0fori:=0;i<len(vin);i++{ifvin[i]==pre[0]{root_son=ibreak}}fori:=0;i<root_son;i++{left_vin=append(left_vin,vin[i])left_pre=append(left_pre,pre[i+1])}fori:=root_son+1;i<len(vin);i++{right_vin=append(right_vin,vin[i])right_pre=append(right_pre,pre[i])}root.Left=reConstructBinaryTree(left_pre,left_vin)root.Right=reConstructBinaryTree(right_pre,right_vin)returnroot}第二种写法,差不多,没第一个好想funcrebuild(pre[]int,pre_leftint,pre_rightint,vin[]int,vin_leftint,vin_rightint)*TreeNode{if(pre_left>pre_right){returnnil}varroot=&TreeNode{pre[pre_left],nil,nil}fori:=vin_left;i<=vin_right;i++{ifvin[i]==root.Val{root.Left=rebuild(pre,pre_left+1,pre_left+i-vin_left,vin,vin_left,i-1)root.Right=rebuild(pre,pre_left+i-vin_left+1,pre_right,vin,i+1,vin_right)}}returnroot}funcreConstructBinaryTree(pre[]int,vin[]int)*TreeNode{//writecodeherereturnrebuild(pre,0,len(pre)-1,vin,0,len(vin)-1)}
一、go语言基础(1)一个包怎么调用另一包的函数,go语言中公有性和私有性怎么表达?函数大写表示公有,小写表示私有(2)简单的介绍一下闭包使用场景,优缺点?首先闭包的特点是当闭包函数引用外部变量的时候,会把这个变量放在堆中,所以在内部函数引用外部函数的变量时,值不会被释放packagemainimport"fmt"funcexternalFunc(countint)func()int{returnfunc()int{count++returncount}}funcmain(){//这时候的external是个函数,externalFunc函数返回了个匿名函数external:=externalFunc(1)//这时候的count==1fmt.Println(external())//输出2fmt.Println(external())//输出3fmt.Println(external())//输出4}用法1.函数计数器//函数计数器funccounter(ffunc())func()int{n:=0returnfunc()int{f()n+=1returnn}}//测试的调用函数funcfoo(){fmt.Println("callfoo")}funcmain(){cnt:=counter(foo)cnt()cnt()cnt()fmt.Println(cnt())}/*输出结果:callfoocallfoocallfoocallfoo4*/2)装饰器和中间件,即函数作为参数传递funcwrapping(ffunc()string){fmt.Println("domywork...")fmt.Println("wrappingfunction:",f())fmt.Println("myworkfinished!")}funcsayHello()string{return"Hello!"}funcsayByeBye()string{return"ByeBye!"}funcmain(){wrapping(sayHello)wrapping(sayByeBye)}/*输出:domywork...wrappingfunction:Hello!myworkfinished!domywork...wrappingfunction:ByeBye!myworkfinished!*/(3)map和arraymake的使用区别?go语言中new和make是内置函数,主要用来创建分配类型内存。new(T)创建一个没有任何数据的类型为T的实例,并返回该实例的指针;make(T,args)只能创建slice,map,channel,并返回一个有初始值args(非零)的T类型的实例,非指针。二者都是内存的分配(堆上),但是make只用于slice、map、channel的初始化,而new用于类型的内存分配,并且内存内置为0.make返回的还是这三个引用类型本身;而new返回的是指向类型的指针。slice和array接近,不过更加的灵活,可以在新的元素加入的时候增加长度。slice总是指向底层的一个array。slice是一个指向array的指针,这是其与array不同的地方;slice是引用类型,这意味着当赋值某个slice到另外一个变量,两个引用会指向同一个array。例如,如果一个函数需要一个slice参数,在其内对slice元素的修改也会体现在函数调用者中,这和传递底层的array指针类似。append:向slice追加零值或者其他的x值,并且返回追加后的新的slice。如果原来的slice没有足够的容量,那么append会分配一个足够大的,新的slice来存放原来的slice和后面追加的值。因此返回的slice可能和原来的不是指向同一个arraySlice所允许申请的最大容量大小,与当前值类型和当前平台位数有直接关系slice扩容机制1.如果切片的容量小于1024个元素,那么扩容的时候slice的cap就翻番,乘以2;一旦元素个数超过1024个元素,增长因子就变成1.25,即每次增加原来容量的四分之一。2.如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。make创建make([]T,length,capacity)slice1:=make([]T,length)length表示slice中已经使用的数据长度capacity表示slice中指针执行的数组容量,如果缺省capacity和length大小相等(4)defer的工作模式go的defer语句是用来延迟执行函数的,而且延迟发生在调用函数return之后,同时defer的执行顺序和栈类似,先进后出(5)匿名函数的使用(难点)常用的感觉就是闭包和gofunc协程了,再说吧。。。。。二、面向对象和并发(1)go语言如何表现继承go中没有extends关键字,所以没有原生级别的继承。go本质上是使用interface实现的,是使用组合来实现继承,用组合来代替继承。在go的结构体struct,通过匿名成员的方式实现继承,比如,Student继承了MentypeMenstruct{namestringageint}typeStudentstruct{Menscoreint}对于接口interface,通过直接引入另一接口的方式实现继承,比如,ReadWriter继承了Reader和WritertypeReaderinterface{}typeWriterinterface{}typeReadWriterinterface{ReaderWriter}(2)接口的优点,使用场景。实现代码解耦(3)并发通信采用什么消息机制。goroutine+channel数据共享
funcfindRepeatNumber(nums[]int)int{n:=len(nums)fori:=0;i<n;i++{fori!=nums[i]{//交换之后的新数还是从这开始,继续判断这个数是否需要交换,直到这个num[i]正确if(nums[i]==nums[nums[i]]){//非第一次出现returnnums[i]}else{//第一次出现x:=nums[i]nums[i]=nums[x]nums[x]=x}}}return0}//没搞明白这个原地置换的原理用map或者排序做吧。funcfindRepeatNumber(nums[]int)int{varmapV=make(map[int]int)fori:=0;i<len(nums);i++{_,ok:=mapV[nums[i]]ifok{returnnums[i]}else{mapV[nums[i]]=i}}return-1}