Posts
Proactor 与 Reactor
Reactor模式用于同步I/O,Proactor运用于异步I/O操作 阻塞模型 同步和异步 针对应用程序和内核交互而言 同步:用户进程触发I/O操作,等待或轮询去查看I/O操作是否就绪 异步:用户进程触发I/O操作后,去做自己的事情,I/O操作完成后给予通知 阻塞和非阻塞 进程在访问数据的时候,根据I/O操作的就绪状态采取不同的方式,即读取或写入操作函数的实现方式 阻塞:读取或写入函数将一直等待 非阻塞:读取或写入函数回立即返回一个状态值 常见I/O模型 以小明下载王者农药和打开游戏两个任务为例 同步阻塞 点击下载—盯着下载条—到100%下载完成—打开游戏 同步:等待下载完成的结果通知 阻塞:盯着下载条,没有处理其余任务 用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行 同布非阻塞 点击下载—刷抖音/看B站—时不时返回查看下载进度—下载完成:点击打开游戏 同步:等待下载完成的结果通知 非阻塞:下载过程可以去处理其余任务 用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费 异步阻塞 小明开启了下载完成的震动通知 点击下载—单纯等待,不主动查看—收到震动通知—打开游戏 异步:下载结果以震动的方式告知 阻塞:等待这个震动,没有去做别的 发起I/O后足赛等待,根据到达的通知来进行数据处理 异步非阻塞 小明开启了下载完成的震动通知 点击下载—刷抖音/看B站—收到震动通知—打开游戏 发起I/O后立即返回,等I/O完成后回得到完成通知,用户进程根据通知来进行数据处理 归纳 同步/异步关注的是消息通知的机制 阻塞/非阻塞关注的是程序(线程)等待消息通知时的状态。 Reactor和Proactor 阻塞与非阻塞都可以理解为同步范畴下才有的概念,对于异步,就不会再去分阻塞非阻塞。 Reactor 要求主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生 有的话就立即将事件通知工作线程(逻辑单元)数据的读写,接受新的连接以及处理客户请求均在工作线程中完成; 除此之外,逻辑线程不作任何工作。 中心思想: 将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程/进程阻塞在多路复用器上; 一旦有I/O事件到来或是准备就绪(文件描述符或socket可读、写),多路复用器返回并将事先注册的相应I/O事件分发到对应的处理器中。 Reactor是一种事件驱动机制,和普通函数调用的不同之处在于: 应用程序不是主动的调用某个API完成处理,而是恰恰相反 Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的事件发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。 应用场景 场景: 长途客车在路途上,有人上车有人下车,但是乘客总是希望能够在客车上得到休息。 传统做法: 每隔一段时间(或每一个站),司机或售票员对每一个乘客询问是否下车。 Reactor做法:汽车是乘客访问的主体(Reactor),乘客上车后,到售票员(acceptor)处登记,之后乘客便可以休息睡觉去了,当到达乘客所要到达的目的地时(指定的事件发生,乘客到了下车地点),售票员将其唤醒即可。 组成 Reactor模式是基于事件驱动的分发处理模型 有一个或多个并发输入源,有一个Service Handler,有多个Request Handlers 这个Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler。 Proactor Proactor将所有I/O操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。 事件句柄初始化一个异步读操作,此时该句柄并不在意异步操作结果,而是要获得完成事件而注册 事件多路器等待直到io事件完成 当事件多路器等待io事件时,操作系统在一个并行的内核线程上处理读操作,并将数据放到一个用户定义的缓冲中,并通知事件多路器操作完成。 事件多路器调用事件句柄 事件句柄从用户定义缓冲中获得用户数据并操作,然后开始新的异步操作并将控释返回事件多路器 在Reactor模式中,事件分离者等待某个事件或者可应用或个操作的状态发生(比如文件描述符可读写,或者是socket可读写),事件分离器就把这个事件传给事先注册的处理器(事件处理函数或者回调函数),由后者来做实际的读写操作。 ...
leetcode_946. 验证栈序列
验证栈序列 题目 给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。 示例 1: 1 2 3 4 5 输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1] 输出:true 解释:我们可以按以下顺序执行: push(1), push(2), push(3), push(4), pop() -> 4, push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1 示例 2: 1 2 3 输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2] 输出:false 解释:1 不能在 2 之前弹出。 提示: 1 2 3 0 <= pushed.length == popped.length <= 1000 0 <= pushed[i], popped[i] < 1000 pushed 是 popped 的排列。 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/validate-stack-sequences 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 ...
常用语法记录
最大最小值常量 1 2 3 4 5 const UINT_MIN uint = 0 const UINT_MAX = ^uint(0) const INT_MAX = int(^uint(0) >> 1) const INT_MIN = ^INT_MAX time包:秒、毫秒、纳秒时间戳输出 https://www.junjie.in/article/34 1 2 3 4 fmt.Printf("时间戳(秒):%v;\n", time.Now().Unix()) fmt.Printf("时间戳(纳秒):%v;\n",time.Now().UnixNano()) fmt.Printf("时间戳(毫秒):%v;\n",time.Now().UnixNano() / 1e6) fmt.Printf("时间戳(纳秒转换为秒):%v;\n",time.Now().UnixNano() / 1e9) 类型断言 https://studygolang.com/articles/20041 1 2 3 4 5 i := "1" if v, ok := i.(int); ok { fmt.Println("to int", i) } // string的uid 转 10进制int 类型断言和类型转换 类型转换 语法:<结果类型> := <目标类型> ( <表达式> ) 类型转换是用来在不同但相互兼容的类型之间的相互转换的方式,所以,当类型不兼容的时候,是无法转换的。 例如 各种int类型,[]byte和string类型 类型断言 语法: <目标类型的值>,<布尔参数> := <表达式>.( 目标类型 ) // 安全类型断言 <目标类型的值> := <表达式>.( 目标类型 ) //非安全类型断言 类型断言的本质,跟类型转换类似,都是类型之间进行转换,不同之处在于,类型断言实在接口之间进行。 在switch中 <目标类型的值> := <表达式>.( type )后,case 目标类型 字符串和数组互转 1 2 strings.Split(uidStr, ",") strings.Join(strSlice, ",") 类型转换 int 之间互相转 ...
leetcode_860. 柠檬水找零
柠檬水找零 题目 在柠檬水摊上,每一杯柠檬水的售价为 5 美元。 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。 注意,一开始你手头没有任何零钱。 如果你能给每位顾客正确找零,返回 true ,否则返回 false 。 示例1: 1 2 3 4 5 6 7 8 9 10 11 输入:[5,5,5,10,20] 输出:true 解释: 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。 由于所有客户都得到了正确的找零,所以我们输出 true。 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/lemonade-change 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 示例2: ...
leetcode_496. 下一个更大元素 I
下一个更大元素 I 题目 给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。 nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出-1。 示例 1: 1 2 3 4 5 6 输入: nums1 = [4,1,2], nums2 = [1,3,4,2]. 输出: [-1,3,-1] 解释: 对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。 对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。 对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。 示例2: 1 2 3 4 5 输入: nums1 = [2,4], nums2 = [1,2,3,4]. 输出: [3,-1] 解释: 对于num1中的数字2,第二个数组中的下一个较大数字是3。 对于num1中的数字4,第二个数组中没有下一个更大的数字,因此输出 -1。 注意: ...
leetcode_470. 用 Rand7() 实现 Rand10()
用 Rand7() 实现 Rand10() 题目 已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。 不要使用系统的 Math.random() 方法。 1 2 3 4 5 6 7 8 9 10 11 示例 1: 输入: 1 输出: [7] 示例 2: 输入: 2 输出: [8,4] 示例 3: 输入: 3 输出: [8,1,10] 提示: rand7 已定义。 传入参数: n 表示 rand10 的调用次数。 ...
leetcode_328. 奇偶链表
奇偶链表 题目 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。 请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。 示例 1: 1 2 输入: 1->2->3->4->5->NULL 输出: 1->3->5->2->4->NULL 示例 2: 1 2 输入: 2->1->3->5->6->4->7->NULL 输出: 2->3->6->7->1->5->4->NULL 说明: 应当保持奇数节点和偶数节点的相对顺序。 链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/odd-even-linked-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 思路 遍历,奇偶结点分类,然后接上 解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // h // o d // 1 2 3 4 5 // 1 3 5 2 4 func oddEvenList(head *ListNode) *ListNode { if head == nil || head.Next == nil { return head } odd, dual, dHead := head, head.Next, head.Next for dHead != nil && dHead.Next != nil && odd != nil { odd.Next = dHead.Next odd = odd.Next dHead.Next = odd.Next dHead = dHead.Next } odd.Next = dual return head } // 执行用时 :4 ms, 在所有 Go 提交中击败了88.57%的用户 // 内存消耗 :3.3 MB, 在所有 Go 提交中击败了59.57%的用户
leetcode_动态规划合集
面试题47. 礼物的最大价值 题目 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物? 示例 1: 1 2 3 4 5 6 7 8 输入: [ [1,3,1], [1,5,1], [4,2,1] ] 输出: 12 解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物 提示: 0 < grid.length <= 200 0 < grid[0].length <= 200 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 解答 显而易见的DP问题,求最值,依然是三步走 确定状态 对于每一步的结果 dp[i][j] = A[i][j]+ 左边格子结果dp[i][j-1]或上面格子结果dp[i-1][j] 要求最大, 即求 max{dp[i][j-1], dp[i-1][j]} 转移方程 $dp[i][j] = max{dp[i-1][j], dp[i][j-1]} + A[i][j]$ 起始和边界值 起始值:dp[0][0] = A[0][0] 边界值来自 第一列:只能来自同列上一行 第一行:只能来自同行上一列 问题分析清楚了就可以代码实现了 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 func maxValue(grid [][]int) int { if grid == nil || len(grid)==0 || len(grid[0])==0 { return 0 } // Golang里面初始化一个二维dp还是很难受的 m, n := len(grid)-1, len(grid[0])-1 // dp长度和宽度和原数组保持一致,所以下面要+1 dp := make([][]int, m+1) for k, _ := range dp { dp[k] = make([]int, n+1) } // 起始值 dp[0][0] = grid[0][0] // 边界值 for k, _ := range grid { if k > 0 { dp[k][0] = dp[k-1][0] + grid[k][0] } } for k, _ := range grid[0] { if k > 0 { dp[0][k] = dp[0][k-1] + grid[0][k] } } for i:=1; i<=m; i++ { for j:=1; j<=n; j++ { dp[i][j] = grid[i][j] + max(dp[i-1][j], dp[i][j-1]) } } return dp[m][n] } func max(x, y int) int { if x > y { return x } return y } // 执行用时 :8 ms, 在所有 Go 提交中击败了95.29%的用户 // 内存消耗 :4.4 MB, 在所有 Go 提交中击败了100.00%的用户 面试题 08.02. 迷路的机器人 题目 设想有个机器人坐在一个网格的左上角,网格 r 行 c 列。机器人只能向下或向右移动,但不能走到一些被禁止的网格(有障碍物)。设计一种算法,寻找机器人从左上角移动到右下角的路径。 ...
leetcode_动态规划入门
动态规划一般出现在难度中上的题目中,一直以来都是比较束手无策的! 抽空看了一章“九章算法”的【动态规划专题班】动态规划入门 Introduction to Dynamic Programming,讲的挺好的。下面是笔记 消除恐惧的最好办法就是面对恐惧! 题目特点 计数 有多少种方式走到右下角 有多少种方法选出k个数使得和为sum 求最大最小值 左上角到右下角路径的最大数字和 最长上升子序列长度 求存在性 取石子游戏,先手是否必胜 能不能选出k个数使得和为sum 例题1:Coin Change 题目 拥有硬币三种:2元、5元、7元,每种硬币足够多 买一本书要27元 如何用最少的硬币组合正好付清,不需要双方找钱 分析 最少硬币组合->动态规划 思路1: 尽量用最大的硬币 7 + 7 + 7 = 21 21 + 5 = 26 找不清了 思路2:尽量用大的,最后如果能付清就行 7 + 7 + 7 = 21 21 + 2 + 2 + 2 = 27 结果是6枚硬币 然而正确结果为:7 + 5 + 5 + 5 + 5 = 27 五枚 解题流程 一、确定状态 状态相当于定海神针 简单来说,解动态规划需要开一个数组,数组的每个元素f[i]和f[i][j]代表什么 类型于解数学题中 X、Y、Z代表什么 确定状态需要有两个意识 最后一步 字问题 最后一步 虽然最优策略未知,但是结果肯定知道:K枚硬币 a1, a2, …, ak 面值加起来是 27 ...
leetcode_双指针合集
11. 盛最多水的容器 题目 给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 说明:你不能倾斜容器,且 n 的值至少为 2。 图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/container-with-most-water 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 解答 双指针往中间逼近,更新最大值 逼近原则:哪个比较低就往中间逼近(贪心思维) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func maxArea(height []int) int { low, high := 0, len(height)-1 var max int for low < high { now := min(height[low], height[high])*(high-low) if max < now { max = now } if height[low] < height[high] { low++ } else { high-- } } return max } func min(x, y int) int { if x > y { return y } return x } 面试题 17.11. 单词距离 题目 有个内含单词的超大文本文件,给定任意两个单词,找出在这个文件中这两个单词的最短距离(相隔单词数)。如果寻找过程在这个文件中会重复多次,而每次寻找的单词不同,你能对此优化吗? ...
leetcode_回溯法合集
46. 全排列 46. 全排列 Difficulty: 中等 给定一个 没有重复 数字的序列,返回其所有可能的全排列。 示例: 1 2 3 4 5 6 7 8 9 10 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] Solution1 普通递归 Language: Go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func permute(nums []int) [][]int { if len(nums) == 1 { return [][]int{nums} } var ret [][]int // 结果 for k, v := range nums { // 1. 提取出当前位 // eg: [1, 2, 3] 提取出[1, 3] tmp := make([]int, len(nums)-1) copy(tmp, nums[:k]) copy(tmp[k:], nums[k+1:]) // 2. 拿去求排列 [1, 3] [3, 1] sub := permute(tmp) // 3. 结果放入res返回 for _, s := range sub { // res = [[1,3,2], [3,1,2]] res = append(res, append(s, v)) } } return res } Solution2 回溯 Language: Go ...
leetcode_字符串合集
Golang字符串注意点⚠️ 对字符串 range的时候,v并不是byte类型而是rune(底层为int32) 1 2 3 4 s := "hello world" for _, v := range s { fmt.Printf("%c, %T", v, v) } 字符串直接取某一位,比s[0],类型为byte(底层为uint8) 1 2 3 4 s := "hello world" for i := 0; i < len(s); i++ { fmt.Printf("%c, %T", s[i], s[i]) } 详细原理,见Go语言字符类型(byte和rune) 面试题 01.09. 字符串轮转 题目 字符串轮转。给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成(比如,waterbottle是erbottlewat旋转后的字符串)。 示例1: 1 2 输入:s1 = "waterbottle", s2 = "erbottlewat" 输出:True 示例2: 1 2 输入:s1 = "aa", "aba" 输出:False 提示: 字符串长度在[0, 100000]范围内。 说明: 你能只调用一次检查子串的方法吗? 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/string-rotation-lcci 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 思路 翻转之后的S2,拼接起来肯定是首位相连的,判断是否包含S1 解答 1 2 3 4 5 6 7 func isFlipedString(s1 string, s2 string) bool { if len(s1) != len(s2) { return false } return strings.Contains(s2+s2, s1) } 自己实现Contains,用KMP算法 ...
leetcode_树合集
112. 路径总和 题目 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树,以及目标和 sum = 22, 1 2 3 4 5 6 7 5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1 返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。 112. 路径总和 思路 递归流程: 子节点: 为 nil 直接返回false 为叶子结点,返回是否为 sum == 结点值 遍历左子树,sum-结点值 遍历右子树,sum-结点值 解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */ func hasPathSum(root *TreeNode, sum int) bool { // 出口1 if root == nil { return false } // 出口2 if root.Left == nil && root.Right == nil { return sum == root.Val } return hasPathSum(root.Left, sum-root.Val) || hasPathSum(root.Right, sum-root.Val) } 236. 二叉树的最近公共祖先 题目 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 ...
leetcode_数组
26. 删除排序数组中的重复项 描述 给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。 示例 1: 给定数组 nums = [1,1,2], 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 你不需要考虑数组中超出新长度后面的元素。 示例 2: 给定 nums = [0,0,1,1,1,2,2,3,3,4], 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 你不需要考虑数组中超出新长度后面的元素。 思路 解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 func removeDuplicates(nums []int) int { if len(nums) == 0 { return 0 } var t int for k, v := range nums { if k == 0 || v != nums[k-1] { nums[t] = v t++ } } return t } 73. 矩阵置零 题目 给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用**原地算法。** ...
leetcode_链表合集
141. 环形链表 题目 给定一个链表,判断链表中是否有环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。 示例 1: 1 2 3 4 5 6 输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。 3 > 2 > 0 > 4 |_______| 示例 2: 1 2 3 4 5 输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。 1 > 2 |___| 示例 3: 1 2 3 输入:head = [1], pos = -1 输出:false 解释:链表中没有环。 进阶: ...
leetcode_堆合集
295. 数据流的中位数 思路 使用大根堆(存放小于中位数的值)和小根堆(存放大于中位数的值)接受传入的数 放入时,二者长度差需要保持小于2 最后返回二者长度较大的值 解答
leetcode_876. 链表的中间结点
链表的中间结点 题目 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。 如果有两个中间结点,则返回第二个中间结点。 示例 1: 1 2 3 4 5 输入:[1,2,3,4,5] 输出:此列表中的结点 3 (序列化形式:[3,4,5]) 返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。 注意,我们返回了一个 ListNode 类型的对象 ans,这样: ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL. 示例 2: 1 2 3 输入:[1,2,3,4,5,6] 输出:此列表中的结点 4 (序列化形式:[4,5,6]) 由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。 提示: 给定链表的结点数介于 1 和 100 之间。 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/middle-of-the-linked-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 思路 快慢指针 快指针每次走两步,慢指针每次走一步 快指针到达尾部,慢指针就到达了中间了 解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 /** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func middleNode(head *ListNode) *ListNode { slow, fast := head, head for fast != nil && fast.Next != nil { slow = slow.Next fast = fast.Next.Next } return slow } // 或 func middleNode(head *ListNode) *ListNode { slow, fast := head, head for fast != nil { fast = fast.Next if fast != nil { slow = slow.Next fast = fast.Next } } return slow } // 执行用时 :0 ms, 在所有 Go 提交中击败了100.00%的用户 // 内存消耗 :2.1 MB, 在所有 Go 提交中击败了5.00%的用户
leetcode_232. 用栈实现队列
用栈实现队列 题目 使用栈实现队列的下列操作: push(x) – 将一个元素放入队列的尾部。 pop() – 从队列首部移除元素。 peek() – 返回队列首部的元素。 empty() – 返回队列是否为空。 示例: 1 2 3 4 5 6 7 8 9 10 11 MyQueue queue = new MyQueue(); queue.push(1); queue.push(2); queue.peek(); // 返回 1 queue.pop(); // 返回 1 queue.empty(); // 返回 false 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/implement-queue-using-stacks 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。 ...
Golang_11_网络编程
十一、Go网络编程 Go实现TCP通信 TCP协议 传输控制协议/网间协议 面向连接的、可靠的、基于字节流的传输层协议 因为是面向连接的协议,数据如同流水传输,会存在黏包问题 TCP服务端 每建立一次链接就创建一个goroutine去处理 TCP服务端的处理流程: 监听接口 接受客户端请求建立链接 创建goroutine处理链接 使用net包实现一个TCP的服务端: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package main import ( "bufio" "fmt" "net" ) func process(conn net.Conn) { defer conn.Close() // 关闭连接 for { reader := bufio.NewReader(conn) var buf [128]byte n, err := reader.Read(buf[:]) // 读取数据 if err != nil { fmt.Println("read from client failed, err:", err) break } recvStr := string(buf[:n]) fmt.Println("收到client端发来的数据", recvStr) conn.Write([]byte(recvStr)) } } func main() { listen, err := net.Listen("tcp", "127.0.0.1:20000") if err != nil { fmt.Println("listen failed, err:", err) return } for { conn, err := listen.Accept() // 建立连接 if err != nil { fmt.Println("accept failed, err:", err) continue } go process(conn) } } TCP客户端 连接流程: ...