LeetCode刷题(Go语言实现-持续更新)

最近开始学习Go语言,所以尝试使用Go语言刷题来更好的熟悉这门语言。打算按题库顺序刷LeetCode,因此会长期更新,欢迎大神一同来交流。

1.两数之和

(1)暴力解法:采用暴力方法直接使用二重循环尝试用两数拼凑target,代码如下:
func twoSum(nums []int, target int) []int {
   
     result := make([]int, 0)
     for k, v := range nums {
   
         temp := target - v
         for j := k + 1; j < len(nums); j++ {
   
             if nums[j] == temp {
   
                 result = append(result, k) 
                 result = append(result, j)
                 goto end
         }
     }
  }
   end:
   return result
}

使用result存储符合条件的数组下标。

(2)借助哈希表一次遍历:为降低时间复杂度构建哈希表,以数组值作为哈希表的键,数组索引作为哈希表值构建哈希表。代码如下:

func twoSum(nums []int, target int) []int {
   
     result := make([]int, 0)
     endMap := make(map[int]int)
     for k, v := range nums {
   
         endMap[v] = k
     }
     for k, v := range nums {
   
         temp := target - v
         val, tag := endMap[temp]//val表示哈希表中temp键所对应的值
         if tag && val != k {
   //tag判断哈希表中是否存在temp键,val!=k避免重复取数
             result = append(result, k)
             result = append(result, val)
             return result
         }
     }
     return nil
}

2.两数相加

面试遇到过
分析:由于给定的两个链表表示的数是从低位到高位,因此无需借助栈保存,直接通过构建新链表存储两数的和。代码如下:

func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
   
       var tag, sum int //tag保存进位
       res := &ListNode{
   Val:0}//构建新链表头结点(无实际意义)
       next := res//next节点指向头结点,实现指针移动
       for l1 != nil && l2 != nil {
   //两个链表均未遍历完时
           if tag == 0 {
   
               sum = l1.Val + l2.Val 
           }else{
   
               sum = l1.Val + l2.Val + 1
           }
           if sum >= 10 {
   //判断是否需要进位
               next.Next = &ListNode{
   Val:sum % 10}//计算结果sum保存到新的节点
               tag = 1 //保存进位
           }else{
   
               next.Next = &ListNode{
   Val:sum}
               tag = 0
           }
           next = next.Next
           l1 = l1.Next
           l2 = l2.Next//加和链和新链同步移动
       }
       for l1 != nil {
   //l2已遍历完,l1未遍历完
           if tag == 0 {
   
               sum = l1.Val
           }else{
   
               sum = l1.Val + 1
           }
           if sum >= 10 {
   
               next.Next = &ListNode{
   Val:sum % 10}
               tag = 1
           }else{
   
               next.Next = &ListNode{
   Val:sum}
               tag = 0
           }
           l1 = l1.Next
           next = next.Next
       }
       for l2 != nil {
   //l1已遍历完,l2未遍历完
           if tag == 0 {
   
               sum = l2.Val
           }else{
   
               sum = l2.Val + 1
           }
           if sum >= 10 {
   
               next.Next = &ListNode{
   Val:sum % 10}
               tag = 1
           }else{
   
               next.Next = &ListNode{
   Val:sum}
               tag = 0
           }
           l2 = l2.Next
           next = next.Next
       }
       if tag == 1 {
   //判断是否需要创建最后的进位节点
           next.Next = &ListNode{
   Val:1}
       }
       return res.Next
}

3.无重复字符的最长子串

面试遇到过

(1)暴力解答:二重循环结合哈希表解答,代码如下:

func lengthOfLongestSubstring(s string) int {
   
     max := 0
     if len(s) == 0 || len(s) == 1 {
   
         return len(s)
     }
     for i := 0; i < len(s); i++ {
   
         temp := make(map[byte]int)
         temp[s[i]] = 1//从当前字符开始构建哈希表,记录出现的字符
         cur := 1//从当前字符开始无重复子串长度
         for j := i + 1; j < len(s); j++ {
   
               _, tag := temp[s[j]]//判断s[j]字符是否出现过
               if tag {
   //出现则以当前s[i]开头的子串达到最长
                   if cur > max {
   //判断是否更新max
                      max = cur
                  }
                  break
               }
               temp[s[j]] = 1//s[j]未出现过,cur加1,s[j]加入到哈希表
               cur++ 
               if cur > max {
   
                      max = cur
                  }
         }
     }
     return max
}

(2)双指针+哈希表:通过构建滑动窗口实现一次遍历,其中滑动窗口内的字符均不重复,实现代码如下:

func lengthOfLongestSubstring(s string) int {
   
    max := 0//记录最长不重复子串长度
    Map := make(map[byte]int)//哈希表记录出现过的字符
    i := 0
    j := 1//双指针构建滑动窗口
    if len(s) == 0 || len(s) == 1 {
   
        return len(s)
    }
    Map[s[i]] = 1//s[i]存入哈希表
    for i <= j && i < len(s) && j < len(s) {
   
        val, _ := Map[s[j]]
        if val == 1 {
   //判断s[j]是否在哈希表中即判断是否在当前滑动窗口内出现过
            if j - i > max {
   //s[j]出现,当前符合不重复子串不包括字符s[j]
                max = j - i//更新max
            }
            Map[s[i]] = 0//窗口左侧向右移,删除哈希表中s[i]
           i++
           
        }else {
   
            Map[s[j]] = 1//s[j]未在当前滑动窗口内,记录到哈希表中
            if j - i + 1 > max {
   //当前符合条件子串包括字符s[j]
                max = j - i + 1
            }
            j++//窗口右侧向后移
        }
    }
    return max
}


4.寻找两个有序数组的中位数

分析:题目本身并不困难,很自然的想到归并排序后根据元素奇偶个数来找中位数,但题目要求时间复杂度O(log(m+n)),因此该方法不可行。看到log很自然的想到二分法,同时看到数组是有序的,找中位数问题可以转换成有序数组查找第k小的问题,最终采用分治法+二分法即可求解。代码如下:

func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
   
     m := len(nums1)
     n := len(nums2)
     total := m + n//两个数组总的元素个数
     //排除其中一个数组为空的情况
     if m == 0 {
   
         if n % 2 != 0 {
   //判断奇偶决定中位数
             return float64(nums2[n / 2])
         }else {
   
             return float64(nums2[n / 2] + nums2[n / 2 - 1])/2.0
         }
     }
     if n == 0 {
   
         if m % 2 != 0 {
   
             return float64(nums1[m / 2])
         }else {
   
             return float64(nums1[m / 2] + nums1[m / 2 - 1])/2.0
         }
     }
     if total % 2 == 0 {
   
     //总元素个数若为偶数,则第k/2个和第k/2+1个元素的平均值为中位数,分治求第k小
         tmp := findK(nums1, 0, nums2, 0, total / 2 ) + findK(nums1, 0, nums2, 0, total / 2 + 1)//直接用tmp/2后转float64会下取整,因此先对tmp转float64
         return float64(tmp) / 2
     }else{
   
         //奇数个元素求第k/2+1小即可
         return float64(findK(nums1, 0, nums2, 0, total / 2 + 1))
     }
}

func findK(nums1 []int, start1 int, nums2 []int, start2 int, k int)int{
   
   //查找第k小元素
    if start1>=len(nums1){
   
        //nums1数组已无合适元素,在nums2中第k小即为所求值
        return nums2[start2+k-1]
    }
    if start2>=len(nums2){
   
        return nums1[start1+k-1]
    }

    if k==1 {
   
         //查找最小值时求两个数组首元素最小即可
        return int(math.Min(float64(nums1[start1]),float64(nums2[start2])))
    }

    midA:=math.MaxInt32
    midB:=math.MaxInt32
    if start1+k/2-1 <len(nums1){
   
       //nums1数组中位数
        midA = nums1[start1+k/2-1]
    }
    if start2+k/2-1 <len(nums2){
   
       //nums2数组中位数
        midB = nums2[start2+k/2-1]
    }

    if midA<midB{
   
        //若nums1数组中位数小于nums2数组中位数,则第k小在nums1的k/2位置起的后半部分,在nums2的前半部分
        return findK(nums1, start1+k/2, nums2, start2, k-k/2)
    }else {
   
        return findK(nums1, start1, nums2, start2+k/2, k-k/2)
    }
}

5.最长回文子串

分析:经典的动态规划算法,关键是找状态转移方程。用dp[i][j]表示子串s[i]到s[j]是否为回文子串,若是则标记true,否则标记false,那么,当s[i]==s[j]时,如果dp[i + 1][j - 1]是回文子串,那么dp[i][j]也是回文子串(i代表起始位置,j代表结束为止,dp[i + 1][j - 1]是dp[i][j]起始位置向后,结束位置向前的结果),具体算法如下:

5.
func longestPalindrome(s string) string {
   
    var dp[1000][1000] bool//初始化状态数组
     for i := 0; i < len(s); i++ {
   
         dp[i][i] = true//仅有一个字符是特殊的回文字符串
     }
     start := 0
     end := 0//保存当前符合回文子串的起止位置
     if len(s) == 0 {
   
         return s
     }
     for j := 1; j < len(s); j++ {
   //结束位置j从第二个字符起
         for i := 0; i < j; i++ {
   //开始位置i小于结束位置
             if s[i] == s[j] && (j - i < 2 || dp[i + 1][j - 1]) {
   //j - i < 2表示只有一个字符的特殊回文子串
                 dp[i][j] = true
                 if j - i > end - start {
   //判断是否需要更新回文子串
                     start = i
                     end = j
                 }
             }else{
   
                 dp[i][j] = false
             }
         }
     }
     return s[start : end + 1]
}

6.Z字形变换

没什么好说的,直接上代码

func
  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值