【CodeTop】TOP 100 刷题 1-10

news2024/11/19 16:36:00

文章目录

  • 1. 无重复字符的最长子串
    • 题目描述
    • 代码与思路
  • 2. 反转链表
    • 题目描述
    • 代码与解题思路
  • 3. LRU 缓存
    • 题目描述
    • 代码与解题思路
  • 4. 数组中的第K个最大元素
    • 题目描述
    • 代码与解题思路
  • 5. K 个一组翻转链表
    • 题目描述
    • 代码与解题思路
  • 6. 三数之和
    • 题目描述
    • 代码与解题思路
  • 7. 最大子数组和
    • 题目描述
    • 代码与解题思路
  • 8. 手撕快排
    • 题目描述
    • 代码与解题思路
  • 9. 两数之和
    • 题目描述
    • 代码与解题思路
  • 10. 最长回文子串
    • 题目描述
    • 代码与解题思路

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

题目链接:3. 无重复字符的最长子串

题目描述

代码与思路

func lengthOfLongestSubstring(s string) int {
    win := map[byte]int{}
    slow, fast, ans := 0, 0, 0
    for fast < len(s) {
        for win[s[fast]] == 1 { // 字符不能重复,如果重复就得将重复的字符出窗口
            win[s[slow]]--
            slow++
        }
        ans = max(ans, fast-slow+1)
        win[s[fast]]++
        fast++
    }
    return ans 
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

我这里使用的是传统的滑动窗口的解题思路,根据题目条件设置出窗口,每轮遍历将一个新的值入窗口,朴素,但是思路简单好用。足够了。

2. 反转链表

题目链接:206. 反转链表

题目描述

代码与解题思路

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseList(head *ListNode) *ListNode {
    var pre *ListNode = nil // 这里注意一下, 不能用 pre := nil, 语法不支持
    var cur *ListNode = head
    for cur != nil {
        tmp := cur.Next
        cur.Next = pre
        pre = cur
        cur = tmp
    }
    return pre
}

这里要注意一下,反转链表开始遍历的时候是 pre,cur 一前一后,而 go 语言不支持 pre := nil 这个操作,所以得用 var pre *ListNode,别搞错了

3. LRU 缓存

题目链接:146. LRU 缓存

题目描述

代码与解题思路

type entry struct {
    key, value int
}


type LRUCache struct {
    capacity  int
    list      *list.List
    keyToNode map[int]*list.Element
}


func Constructor(capacity int) LRUCache {
    return LRUCache {
        capacity,
        list.New(),
        map[int]*list.Element{},
    }
}


func (c *LRUCache) Get(key int) int {
    node := c.keyToNode[key]
    if node == nil { // 没找到这本书
        return -1
    }
    c.list.MoveToFront(node)
    return node.Value.(entry).value
}


func (c *LRUCache) Put(key int, value int)  {
    if node := c.keyToNode[key]; node != nil { // 有这本书
        node.Value = entry {key, value}
        c.list.MoveToFront(node)
        return
    }
    c.keyToNode[key] = c.list.PushFront(entry{key, value}) // 新书,放在最上面
    if len(c.keyToNode) > c.capacity { // 书数量超过 capacity
        delete(c.keyToNode, c.list.Remove(c.list.Back()).(entry).key) // 去掉最后一本书
    }
}


/**
 * Your LRUCache object will be instantiated and called as such:
 * obj := Constructor(capacity);
 * param_1 := obj.Get(key);
 * obj.Put(key,value);
 */

用的是双向链表 + 哈希的方法:

  1. get 把缓存取出,放在链表表头
  2. put 放入新缓存,如果已经存在 key,就取出放在表头,并更新 value;如果不存在 key,就放在表头(如果超过 capacity 就把链表尾移除)

双向链表能让我们操作头尾的时候是 O(1) 的复杂度,而哈希能让我们在 get 查找的时候是 O(1) 的复杂度。

代码中需要注意的点是:

  1. 将节点移到表头可以用 go 语言 list 中自带的 MoveToFront 方法,list 相关可以去学习这一篇文章:Go 基础:list 的介绍与使用
  2. 在删除元素的时候,我们使用的两个数据结构:map 和 list 中的元素都要记得删除,所以代码最后一段,我们 delete 和 Remove 同时在使用

4. 数组中的第K个最大元素

题目链接:215. 数组中的第K个最大元素

题目描述

代码与解题思路

var num []int

func findKthLargest(nums []int, k int) int {
    num = nums
    return qselect(0, len(nums)-1, len(num)-k)
}

func qselect(left, right, k int) int { // 双指针快排模板
    mid := getmid(left, right) // 三数取中(也可以随机选数)
    swap(&num[left], &num[mid]) // 选出来的数换 key(基准值)
    key, prev, cur := left, left, left+1
    for cur <= right { // 这里的 right 其实就是数组最右边的下标
        if num[cur] < num[key] {
            prev++
            swap(&num[cur], &num[prev])
        }
        cur++
    }
    swap(&num[key], &num[prev])
    key = prev // key 的左区间比 key 小, 右区间比 key 大, 再分两路递归
    if key < k {
        qselect(key+1, right, k)
    } else if key > k {
        qselect(left, key-1, k)
    }
    return num[k] // 如果 key 的位置正好是 k, 那就找到要取的数了
}

func getmid(left, right int) int { // 三数取中
    mid := (left+right)/2
    if num[mid] > num[left] {
        if num[right] > num[mid] {
            return mid
        } else if num[right] < num[left] {
            return left
        } else {
            return right
        }
    } else {
        if num[right] > num[left] {
            return left
        } else if num[right] < num[mid] {
            return mid
        } else {
            return right
        }
    }
    return -1
}

func swap(a, b *int) {
    *a, *b = *b, *a
}

这道题有两个思路,一个是用快排来解,通过快排的基准值 key 来锁定倒数第 k 个数,但是 LeetCode 最近新添加了一个全是 1 的样例,如果说快排遇到有序数组可以用随机选数或者三数取中来优化,那么,遇到全是同一个数的场景,快排当场退化成 N 方复杂度,算是快排的缺陷了。

我上面的使用的就是快排,所以现在 LeetCode 最后一个全是 1 的样例过不去(别尬黑,LeetCode 官方自己的题解都过不去)

我使用的是快排的双指针法,代码还是比较好理解的,直接读代码就行。

那这个时候就只能用堆排来做了,虽然复杂度是 O(N*logk) 但是 logk 几乎可以忽略不计,所以也能当做是 O(N) 的复杂度,下面是用堆排的算法:

func findKthLargest(nums []int, k int) int {
    n := len(nums)
    buildMaxHeap(nums, n) // 建堆操作, 我们需要一大堆来执行堆排
    for end := n-1; end > 0; end-- { // 排序成升序数组(堆排)
        swap(&nums[end], &nums[0]); // 头尾交换
        AdjustDown(nums, end, 0) // 向下调整(向下调整的左右子树必须是大堆)
    }
    return nums[n-k] // 返回倒数第 k 位置的数
}

func buildMaxHeap(nums []int, n int) { // 建一个大堆
    for i := (n-2)/2; i >= 0; i-- { // 不需要遍历叶子节点(如果不明白就画个图)
        AdjustDown(nums, n, i)
    }
}

func AdjustDown(nums []int, n, parent int) { // 向下调整操作
    child := parent*2+1 // (左孩子)子节点 = 父节点 * 2 + 1, 右孩子是 * 2 + 2
    for child < n { // 子节点需要在堆(数组)的范围内
        if (child+1) < n && nums[child+1] > nums[child] { // 比较左右孩子大小
            child++ // 右孩子大, 那就往右孩子那边调整
        }
        if nums[child] > nums[parent] { // 如果子节点大于父节点, 就交换
            swap(&nums[child], &nums[parent]);
            parent = child // child 作为新的父节点
            child = parent*2+1 // 找下一个孩子
        } else {
            break // 这部分已经是大堆了
        }
    }
}

func swap(a, b *int) {
    *a, *b = *b, *a
}

之前因为一直没有总结,所以堆排我总是忘记怎么做,今天我就要把堆排拿下。

首先是建堆操作,比如说:[3,1,2,5,6,4] 这个数组建堆:

func buildMaxHeap(nums []int, n int) { // 建一个大堆
    for i := (n-2)/2; i >= 0; i-- { // 不需要遍历叶子节点(如果不明白就画个图)
        AdjustDown(nums, n, i)
    }
}

建堆操作的代码,i 不需要从叶子节点开始,为什么 -2,就是为了让 i 能从 2 这个节点,也就是父节点开始遍历,不需要从叶子节点开始,(n-2)/2 无论是什么情况,都能从最右边的第一个父节点开始。

然后通过向下调整操作,也就这段代码:

func AdjustDown(nums []int, n, parent int) { // 向下调整操作
    child := parent*2+1 // (左孩子)子节点 = 父节点 * 2 + 1, 右孩子是 * 2 + 2
    for child < n { // 子节点需要在堆(数组)的范围内
        if (child+1) < n && nums[child+1] > nums[child] { // 比较左右孩子大小
            child++ // 右孩子大, 那就往右孩子那边调整
        }
        if nums[child] > nums[parent] { // 如果子节点大于父节点, 就交换
            swap(&nums[child], &nums[parent]);
            parent = child // child 作为新的父节点
            child = parent*2+1 // 找下一个孩子
        } else {
            break // 这部分已经是大堆了
        }
    }
}

就能建出一个大堆:

最后再用堆排序操作,把数组排成一个升序:

func findKthLargest(nums []int, k int) int {
    n := len(nums)
    buildMaxHeap(nums, n) // 建堆操作, 我们需要一大堆来执行堆排
    for end := n-1; end > 0; end-- { // 排序成升序数组(堆排)
        swap(&nums[end], &nums[0]); // 头尾交换
        AdjustDown(nums, end, 0) // 向下调整(向下调整的左右子树必须是大堆)
    }
    return nums[n-k] // 返回倒数第 k 位置的数
}

因为是一个大堆,所以进行向下调整操作的时候就能够将堆中最大的数调整到堆顶,然后将堆顶(也就是最大的数放到数组尾,让 end–,也就是让堆的 size–,这样向下调整的时候就调整不到放在数组尾的数了),以此类推,就能将数组排序成升序数组。

5. K 个一组翻转链表

题目链接:25. K 个一组翻转链表

题目描述

代码与解题思路

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseKGroup(head *ListNode, k int) *ListNode {
    phead := &ListNode{Next: head}
    pre, end := phead, phead
    for end.Next != nil {
        for i := 0; i < k && end != nil; i++ { // 一段反转区间的尾部
            end = end.Next
        }
        if end == nil { // 最后这段区间不完整, 不用再遍历了
            break
        }
        start, next := pre.Next, end.Next
        end.Next = nil
        pre.Next = reverse(start) // 前面让 end.Next = nil, 再用反转就能反转这段区间
        start.Next = next // next 保存了下一段区间的第一个节点
        pre = start
        end = pre // pre 和 end 再次回到类似哨兵位的位置
    }
    return phead.Next
}

func reverse(head *ListNode) *ListNode { // 反转链表(一模一样的代码)
    var pre *ListNode = nil
    var cur *ListNode = head
    for cur != nil {
        next := cur.Next
        cur.Next = pre
        pre = cur
        cur = next
    }
    return pre
}

代码的注释非常的详细,具体的思路就是通过 end 找到需要反转的区间尾,用 next 记录下一个区间,然后反转当前锁定的区间,然后接通反转后的区间和未处理的区间,以此类推。

这道题主要就是考察对操作链表代码的掌控能力。

6. 三数之和

题目链接:15. 三数之和

题目描述

代码与解题思路

func threeSum(nums []int) (ans [][]int) {
    n := len(nums)
    sort.Ints(nums)
    for i, v := range nums[:n-2] {
        if i != 0 && nums[i-1] == v { // 去重操作
            continue
        }
        if v+nums[i+1]+nums[i+2] > 0 { // 两个优化操作
            break
        }
        if v+nums[n-1]+nums[n-2] < 0 {
            continue
        }
        left, right := i+1, n-1
        for left < right { // 双指针匹配
            s := v+nums[left]+nums[right]
            if s < 0 {
                left++
            } else if s > 0 {
                right--
            } else {
                ans = append(ans, []int{v, nums[left], nums[right]})
                for left < right && nums[left] == nums[left+1] { // 两段去重操作
                    left++
                }
                for left < right && nums[right] == nums[right-1] {
                    right--
                }
                left++
                right--
            }
        }
    }
    return ans
}

需要注意的点:

  1. 三段去重的逻辑
  2. 两段优化的逻辑

下次写三数之和的时候,先把主体代码写完,能跑过了再去把两段可以优化的点给做优化。

7. 最大子数组和

题目链接:53. 最大子数组和

题目描述

代码与解题思路

func maxSubArray(nums []int) (ans int) {
    ans = -10000
    dp := make([]int, len(nums)+1)
    for i := 1; i <= len(nums); i++ {
        dp[i] = max(nums[i-1], dp[i-1]+nums[i-1])
        ans = max(ans, dp[i])
    }
    return ans
}

这道题我一开始还想着是滑动窗口,其实不是啊,别给搞蒙了,这个是动态规划的题目,具体思路是这样的:

dp 数组存的是这样的:i 位置存的数是 i 位置以及前面位置的最大和,这样我们就能通过 dp[i] = max(nums[i-1], dp[i-1]+nums[i-1]) 也就是通过上一位置取得的最大和来计算当前位置的最大和,利用之前位置算出的值,这就是动态规划的思想

8. 手撕快排

题目链接:912. 排序数组

这道题是个补充题,就是来考手撕快排的。

题目描述

代码与解题思路

func sortArray(nums []int) []int {
    quickSort(0, len(nums)-1, nums)
    return nums
}

func quickSort(left, right int, nums []int) {
    if left > right {
        return
    }
    v := getmid(left, right, nums)
    swap(&nums[v], &nums[left])
    key, pre, cur := left, left, left+1
    for cur <= right { // 这里注意一下, 用的是 right 做边界
        if nums[cur] < nums[key] {
            pre++
            swap(&nums[pre], &nums[cur])
        }
        cur++
    }
    swap(&nums[pre], &nums[key])
    key = pre
    quickSort(left, key-1, nums)
    quickSort(key+1, right, nums)
}

func getmid(left, right int, num []int) int { // 三数取中
    mid := (left+right)/2
    if num[mid] > num[left] {
        if num[right] > num[mid] {
            return mid
        } else if num[right] < num[left] {
            return left
        } else {
            return right
        }
    } else {
        if num[right] > num[left] {
            return left
        } else if num[right] < num[mid] {
            return mid
        } else {
            return right
        }
    }
    return -1
}

func swap(a, b *int) {
    *a, *b = *b, *a
}

没什么可说的,多练习便是,多手撕几次,自然就记住了(还记得我当年的爱好就是没事手撕个快排呢)

9. 两数之和

题目链接:1. 两数之和

题目描述

代码与解题思路

func twoSum(nums []int, target int) []int {
    mp := map[int]int{}
    for i, v := range nums {
        if j, ok := mp[target-v]; ok { // 哈希表中有值 = target-v
            return []int{i, j}
        }
        mp[v] = i
    }
    return nil
}

两种解法,暴力 N 方,哈希 O(N),把哈希的解法记住就行。

10. 最长回文子串

题目链接:5. 最长回文子串

题目描述

代码与解题思路

func longestPalindrome(s string) string {
    if s == "" {
        return ""
    }
    start, end := 0, 0 // 最长的回文串区间
    for i, _ := range s {
        l1, r1 := extend(i, i, s)   // "aba"
        l2, r2 := extend(i, i+1, s) // "bb"
        if r1-l1 > end-start {
            start, end = l1, r1
        }
        if r2-l2 > end-start {
            start, end = l2, r2
        }
    }
    return s[start:end+1]
}

func extend(l, r int, s string) (int, int) { // 中心扩散
    for l >= 0 && r < len(s) && s[l] == s[r] {
        l--
        r++
    }
    return l+1, r-1
}

题解有动态规划,但是看不懂,用中心扩散就行了,把中心扩散的 go 版本代码记住,大概就没有什么问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1196127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Edge浏览器新建标签页如何更改为指定网址

Edge浏览器新建标签页如何更改为指定网址&#xff1f; 启动时新建标签页 不是说启动时&#xff0c;而是加号新建标签页时候 启动时 新建标签页 New Tab Changer 可以了 如果没有需要应用商店下载 参考文章

Qframework 中超级方便的kitres

using QFramework; using System.Collections; using System.Collections.Generic; using UnityEngine;public class TestResKit : MonoBehaviour {ResLoader mResLoader ResLoader.Allocate();private void Awake(){}/// <summary>/// 每一个需要加载资源的单元(脚本,界…

FiRa标准——MAC实现(二)

在IEEE 802.15.4z标准中&#xff0c;最关键的就是引入了STS&#xff08;加扰时间戳序列&#xff09;&#xff0c;实现了安全测距&#xff0c;大大提高了测距应用的安全性能。在FiRa的实现中&#xff0c;其密钥派生功能是非常重要的一个部分&#xff0c;本文首先对FiRa MAC中加密…

element-plus 循环生成的多个input输入框如何校验

我们知道正常写出来的input输入框如何校验&#xff0c;那循环出来的输入框应该怎么校验咧&#xff0c;这里就教大家如何的去校验通过循环出来的输入框。 首先先看单个的input如何做校验 <template><div><el-form ref"ruleFormRef" :model"ruleF…

面试题:说一下公司常用MySQL分库分表方案

文章目录 一、数据库瓶颈1、IO瓶颈2、CPU瓶颈 二、分库分表1、水平分库2、水平分表3、垂直分库4、垂直分表 三、分库分表工具四、分库分表步骤五、分库分表问题1、非partition key的查询问题2、非partition key跨库跨表分页查询问题3、扩容问题 六、分库分表总结 一、数据库瓶颈…

使用MVS-GaN HEMT紧凑模型促进基于GaN的射频和高电压电路设计

标题&#xff1a;Facilitation of GaN-Based RF- and HV-Circuit Designs Using MVS-GaN HEMT Compact Model 来源&#xff1a;IEEE TRANSACTIONS ON ELECTRON DEVICES&#xff08;19年&#xff09; 摘要—本文阐述了基于物理的紧凑器件模型在研究器件行为细微差异对电路和系统…

Linux中的粘滞位

目录 粘滞位1、作用2、为什么添加粘滞位3、演示粘滞位的使用方法和效果 粘滞位 1、作用 为了多人协作写进行文件创作时&#xff0c;other用户没有办法将文件删除&#xff0c;只有超级管理员、该目录的所有者、该文件的所有者他们可以删除。 2、为什么添加粘滞位 你想在进行…

rabbitmq延迟队列发送与取消

安装延迟插件 根据rabbitmq的版本下载插件版本 # 延迟队列插件下载地址 https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases# 将本地下载好的插件复制到docker里 # docker cp rabbitmq_delayed_message_exchange-3.9.0.ez 容器名:/plugins docker cp r…

数据结构预算法--链表(单链表,双向链表)

1.链表 目录 1.链表 1.1链表的概念及结构 1.2 链表的分类 2.单链表的实现(不带哨兵位&#xff09; 2.1接口函数 2.2函数的实现 3.双向链表的实现&#xff08;带哨兵位&#xff09; 3.1接口函数 3.2函数的实现 1.1链表的概念及结构 概念&#xff1a;链表是一种物理存储结…

基于SSM的学生就业管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

K8S知识点(八)

&#xff08;1&#xff09;实战入门-Label 通过标签实现Pod的区分&#xff0c;说白了就是一种标签选择机制 可以使用命令是否加了标签&#xff1a; 打标签&#xff1a; 更新标签&#xff1a; 筛选标签&#xff1a; 修改配置文件&#xff0c;重新创建一个pod 筛选&#xff1…

手写C++ 实现链表的反转、删除、合并

目录 一、手写List成员方法 1.1 打印链表 1.2 删除链表节点 1.3 链表中倒数第k个节点 1.4 反转链表 1.5 合并两个排序链表 二、完整代码 一、C实现链表成员方法 在上一篇博客《手写链表C》&#xff0c;实现了基本的List类。在面试中&#xff0c;经常被问到List如何反转、…

代码随想录训练营Day2:1.有序数组的平方 2.长度最小的子数组3,螺旋矩阵

本专栏内容为&#xff1a;代码随想录训练营学习专栏&#xff0c;用于记录训练营的学习经验分享与总结。 文档讲解&#xff1a;代码随想录 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓…

基于SSM的超市库存商品管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

dbeaver连接别人的数据库没有表

1.概念 非缺省的数据库&#xff1a; 通常是指在一个数据库管理系统&#xff08;DBMS&#xff09;中&#xff0c;除了系统默认创建的数据库之外的其他用户创建或自定义的数据库。许多数据库系统在安装后会创建一个默认数据库&#xff0c;例如MySQL中的mysql数据库&#xff0c;…

【lib.dll.a.so】Windows和Linux两个系统下的库文件

1.静态库&&动态库 Windows平台下&#xff1a;静态库后缀为.lib&#xff0c;动态库后缀为.dll Linux平台下&#xff1a;静态库格式为lib**.a&#xff0c;动态库格式为lib**.so 谈论两者区别之前&#xff0c;需要对程序编译和运行有一个大致认识&#xff1a; 代码想要…

765. 情侣牵手(困难)

首先不考虑已经正确坐在一起的组合在没有坐在一起的组合中&#xff0c;只有当两对情侣互相配对时只需要一次交换操作就可以使得两对情侣完成匹配&#xff0c;其余情况交换数等于情侣对数可以把所有情侣看成一个大集合&#xff0c;这个大集合是可以拆成若干小集合的&#xff0c;…

QGIS导出Geoserver样式加载

1.在QGIS中加载并设计样式 加载数据之后按F7键即可打开样式编辑器 可以右键图层&#xff0c;点击属性中的符号化&#xff0c;有一个“基于规则”&#xff0c;可以设定规则或者比例尺范围。可以实现一定比例尺缩放可见或不可见的效果。 2.设计完样式之后右键图层导出 选择保…

欧拉角(横滚角、俯仰角、偏航角)、旋转矩阵、四元数的转换与解决万向节死锁

1、概述 物体的位姿&#xff08;位置和方向&#xff09;的描述方法一般使用两个坐标系来表示&#xff0c;一个是世界坐标系或地面坐标系&#xff0c;这里我都叫做地面坐标系吧&#xff0c;属于参考坐标系&#xff1b;另一个是自身的坐标系&#xff0c;以飞机为例来讲述一些常见…

Linux学习教程(第一章 简介)4

第一章 简介 十一、Linux的主要应用领域有哪些? Linux 似乎在我们平时的生活中很少看到,那么它应用在哪些领域呢?其实,在生活中随时随地都有 Linux 为我们服务着。 1、网站服务器 用事实说话!访问国际知名的 Netcraft 网站 http:// www.netcraft.com,在 "Whats …