Leetcode记录库数据结构篇之一:链表

【LeetCode刷题——数据结构篇】 2022寒假LeetCode刷题——Task01:链表 简介:本人大一学过C++,此后几年间几乎没有使用过,可以称得上是小白了。所以此次刷题会是比较困难的过程,同时也是通过这次的组队学习捡拾起以前的知识,更重要是扩充和巩固。所以这篇blog篇幅可能较长且也有基础型知识的回顾,也是我在做题过程中一步步完成的记录。 一、数组基础题目 (题号:0189)旋转数组 题目:给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 首先要弄清楚的点: vector和array的区别 双指针法 ... 阅读详情

结论

记录一些很主观的东西,有些想法归根结底可能还是自己的实力不够。
与链表有关的算法题,一般需要进行以下考虑:

1.区分所操作节点的类型:首结点、中间节点、尾节点。
2.如果需要对头结点进行相关操作,那么考虑是否要添加虚拟头结点。
3.如果一个问题涉及到数据的顺序和要进行的操作相逆的话,考虑使用栈结构转存。而且使用数组栈效率高一点。

1 160.相交链表

https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

思路描述

  • 如果两个链表相交的话,那么他们必有交叉的地方,如图:
    在这里插入图片描述
    而且不会是这样的交叉:
    在这里插入图片描述
    因为链表中的节点只会有一个后继节点。 所以有a+c+b = b+c+a,顺序遍历两个链表,当某个链表结束时,开始遍历另一个链表,直到节点相等。

代码实现

  • java代码:
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        ListNode a = headA;
        ListNode b = headB;
        while (a != b) {
            a = a != null ? a.next : headB;
            b = b != null ? b.next : headA;
        }

        return a;
    }
}

注意事项

  • 无

拓展延伸

  • 如果只是判断是否存在交点,那么就是另一个问题,即 编程之美 3.6 的问题。
    有两种解法:把第一个链表的结尾连接到第二个链表的开头,看第二个链表是否存在环;或者直接比较两个链表的最后一个节点是否相同。

2 206.反转链表

https://leetcode-cn.com/problems/reverse-linked-list/

思路描述

  • 2019年考研408试卷原题,如果我那年复习的时候不那么投机取巧,这道题就能做上了,明明这么简单。55555……
    头插法原地逆置链表,时间复杂度O(n),空间复杂度O(1)。

代码实现

  • java代码:
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null) return head;

        ListNode newhead, temp, node;
        //取下需要逆转的链表的第一个,作为新链表的结尾
        newhead = head;
        head = head.next;
        //结尾置空
        newhead.next = null;

        while(head != null){
            node = head;//先记录下当前需要操作的节点
            temp = head.next;//记录下下一个要操作的节点
            node.next = newhead;//当前操作节点接到逆转后的链表的头部
            newhead = node;//当前操作节点成为逆转后链表的新头
            head = temp;//未逆转链表的新头
        }
        return newhead;
    }
}

注意事项

  • 1.java中对于链表节点“=”等号赋值好像是两个变量公用一个内存单元,所以说修改一个变量的next,另一个变量的next也已经变了。

拓展延伸
递归的解法:

public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    ListNode next = head.next;
    ListNode newHead = reverseList(next);
    next.next = head;
    head.next = null;
    return newHead;
}

3 21.归并有序链表

https://leetcode-cn.com/problems/merge-two-sorted-lists/

思路描述
woshishabi.

代码实现

  • java代码:
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null) return l2;
        if(l2 == null) return l1;

        ListNode tempNode, newhead, newtail;
        if(l1.val <= l2.val){
            newhead = l1;
            newtail = l1;
            l1 = l1.next;
        }else{
            newhead = l2;
            newtail = l2;
            l2 = l2.next;
        }

        while(l1 != null && l2 != null){
            while((l1 != null && l2 != null) && l1.val <= l2.val){
                newtail.next = l1;
                newtail = l1;
                l1 = l1.next;
            }

            while((l1 != null && l2 != null) && l2.val <= l1.val){
                newtail.next = l2;
                newtail = l2;
                l2 = l2.next;
            }
        }
        newtail.next = (l2 == null)? l1 : l2;

        return newhead;
    }
}

注意事项

  • 1.脑袋不清楚的时候别写代码!!!
  • 2.脑袋不清楚的时候别写代码!!!
  • 3.脑袋不清楚的时候别写代码!!!

拓展延伸
递归的解法:

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    if (l1 == null) return l2;
    if (l2 == null) return l1;
    if (l1.val < l2.val) {
        l1.next = mergeTwoLists(l1.next, l2);
        return l1;
    } else {
        l2.next = mergeTwoLists(l1, l2.next);
        return l2;
    }
}

4 83. 删除排序链表中的重复元素

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/

思路描述
这也算是双指针吧,一快一慢,如果快指针的val和慢指针的val相同,快指针就向后移。直到链表结束。

代码实现
java代码:

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode l1 = head, l2 = head;
        while(l2 != null){
            while(l2 != null && l1.val == l2.val){
                l2 = l2.next;
            }
            l1.next = l2;
            l1 = l2;
        }
        return head;
    }
}

注意事项

  • 1.在取指针的值之前,需要注意先确定指针是否为空。

拓展延伸
递归的解法:

public ListNode deleteDuplicates(ListNode head) {
    if (head == null || head.next == null) return head;
    head.next = deleteDuplicates(head.next);
    return head.val == head.next.val ? head.next : head;
}

5 19. 删除链表的倒数第 N 个结点

https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

思路描述

  • 1.删除一个链表的头,我们需要将链表的头改变。
    2.删除一个链表中间的一个节点,我们需要知道这个节点的前一个节点和后一个节点,然后将两者相连。
    3.需要删除的是尾节点,和2一样。 基本原理:双指针,一快一慢,中间相差k步。一个指需被删节点前,一个指链表尾。

代码实现
java代码:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 前置处理
        if(head == null || n == 0) return head;
        else if(head.next == null){
            head = null;
            return head;
        }

        // 确定prior和tail之间的距离
        ListNode prior = head, tail = head;
        do{
            tail = tail.next;
            n--;
        }while(n != 0);

        // 需要删除的是头结点
        if(tail == null){
            return head.next;
        }

        // 否则,双指针同步后移,遍历列表
        while(tail.next != null){
            prior = prior.next;
            tail = tail.next;
        }

        // 删除节点
        prior.next = prior.next.next;

        return head;
    }
}

注意事项

  • 1.在对指针进行操作时,一定要注意区分,节点的类型,链表中的节点分为三种:头结点、中间节点、尾节点,在对他们操作时需要注意。

拓展延伸

  • 无

6 24. 两两交换链表中的节点

https://leetcode-cn.com/problems/swap-nodes-in-pairs/

思路描述

  • 1.先试试递归:如下。但是这样每一层都会定义两个变量,空间复杂度是O(n)了。

代码实现
java代码:

class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null) return head;

        ListNode swap = head, temp = head.next.next;
        head = head.next;
        head.next = swap;
        swap.next = swapPairs(temp);

        return head;
    }
}

思路描述

  • 2.再试试常规解法:
    交换前后两个节点,我们需要知道: (从这里开始走偏了)一个窗口大小为4的链表段的节点指针。(最后还是变递归了,而且还很繁琐)
  • 其实我们需要知道的是,这两个节点的前置节点就好了。交换3号和4节点,我们只需要知道2号节点既可以了。但是,交换1号和2号,我们需要表头节点,没有的话虚拟出一个来。这就有了拓展延伸的方法。

代码实现
java代码:

class Solution {
    public ListNode swapPairs(ListNode head) {
        // 
        if(head == null || head.next == null){
            return head;
        } 
        if(head.next.next == null){
            ListNode swap = head.next;
            swap.next = head;
            head.next = null;
            return swap;
        } 

        if(head.next.next.next == null){
            ListNode swap = head.next, tail = head.next.next;
            swap.next = head;
            head.next = tail;
            return swap;
        } 

        ListNode p1, p2, p4, newhead = head.next, swap;
        while(head.next.next.next != null){
            p1 = head;
            p2 = head.next;
            p4 = head.next.next.next;
            head = head.next.next;

            swap = p2;
            swap.next = p1;
            p1.next = p4;

            if(head == null || head.next == null){
                return newhead;
            } 

            if(head.next.next == null){
                swap = head.next;
                swap.next = head;
                head.next = null;

                return newhead;
            } 

            if(head.next.next.next == null){
                swap = head.next;
                ListNode tail = head.next.next;
                swap.next = head;
                head.next = tail;

                return newhead;
            } 
        } 
        return newhead;
    }
}

注意事项

  • 1.在对指针进行操作时,一定要注意区分,节点的类型,链表中的节点分为三种:头结点、中间节点、尾节点,在对他们操作时需要注意。

拓展延伸

  • 1.在链表头之前加一个虚拟节点,便可迎刃而解。时间复杂度O(n),空间O(1)
class Solution {
    public ListNode swapPairs(ListNode head) {
        // 设置一个链表头结点的前置节点
        ListNode node = new ListNode(-1);
        node.next = head;
        
        ListNode pre = node;
        while (pre.next != null && pre.next.next != null) {
            ListNode l1 = pre.next, l2 = pre.next.next;
            ListNode next = l2.next;
            l1.next = next;
            l2.next = l1;
            pre.next = l2;

            pre = l1;
        }
        return node.next;   
    }
}

7 445. 两数相加 II

题目

思路描述

  • 1.应该是要用递归,倒着从最后一位加过来,因为会涉及到链表头,所以需要新建一个头结点。
  • 不行,递归做不出来
  • 因为本题是需要对 两个链表 同时处理。
  • 也就是说对 两个链表是并行处理的关系。而递归一般只能对一个 变量做递归处理;那么处理2个链表就成了串行处理,无法做累加。也不是不能用递归对两个链表处理,但是里面还涉及到对齐的问题;特别是因为节点数不一样,一个到链表尾,另一个没到,还是需要递归。
  • 也就是说,如果两个链表的节点个数是 一样 ,那就可以把两个链表,抽象成一个来处理。

代码实现
java代码:

思路描述

  • 1.使用栈结构保存链表内容,然后一一相加,使用头插法建立链表进行存储。

代码实现
java代码:

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 引入栈结构
        Stack<Integer> stack1 = listToStack(l1);
        Stack<Integer> stack2 = listToStack(l2);

        // 初始化进位
        int carry = 0, a = 0, b = 0, result;

        // 初始化返回的链表头结点
        ListNode newhead = new ListNode(-1, null);
        while(!stack1.isEmpty() || !stack2.isEmpty() || carry != 0){
            a = stack1.isEmpty() ? 0 : stack1.pop();
            b = stack2.isEmpty() ? 0 : stack2.pop();
            result = a + b + carry;
            carry = result / 10;
            result = result % 10;

            // 头插法建立链表,保证顺序
            ListNode node = new ListNode(result);
            node.next = newhead.next;
            newhead.next = node;
        }

        return newhead.next;
    }

    // 私有方法将链表中的val存放到栈中
    private Stack<Integer> listToStack(ListNode l){
        Stack<Integer> stack = new Stack<>();
        while(l != null){
            stack.push(l.val);
            l = l.next;
        }

        return stack;
    }
}

注意事项

  • 注意事项

拓展延伸

  • 如果一个问题涉及到数据的顺序和要进行的操作相逆的话,考虑使用栈结构转存。而且使用数组栈效率高一点。
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 引入数组栈结构
        int stack1[] =new int[100], top1 = -1;
        while(l1 != null){
            stack1[++top1] = l1.val;
            l1 = l1.next;
        }

        int stack2[] = new int[100], top2 = -1;
        while(l2 != null){
            stack2[++top2] = l2.val;
            l2 = l2.next;
        }

        // 初始化进位
        int carry = 0, a = 0, b = 0, result;

        // 初始化返回的链表头结点
        ListNode newhead = new ListNode(-1, null);
        while(top1 != -1 || top2 != -1 || carry != 0){
            a = (top1 == -1) ? 0 : stack1[top1--];
            b = (top2 == -1) ? 0 : stack2[top2--];
            result = a + b + carry;
            carry = result / 10;
            result = result % 10;

            //System.out.println(top1);

            // 头插法建立链表,保证顺序
            ListNode node = new ListNode(result);
            node.next = newhead.next;
            newhead.next = node;
        }

        return newhead.next;
    }
}

8 234. 回文链表

题目

思路描述

  • 回文链表,从中间分开两边是对称的。这需要对链表的长度进行区分是偶数还是奇数。
  • 1.偶数:使用双指针,快指针一次移动两个位置,慢指针一次一个,同时使用栈结构将内容存下来,然后从慢指针这里继续往后遍历,同时出栈判断元素是否相同。
  • 2.奇数:原理相同,不过在判断元素是否相同时,栈结构要先出栈一个。
  • 时空间复杂度为O(n)和O(n);

代码实现
java代码:

class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head.next == null) return true;

        // 
        ListNode fast = head, slow = head;
        int i = 0;
        Stack<Integer> stack = new Stack<>();
        while(fast != null){
            i++;
            stack.push(slow.val);
            // fast后移两个并进行判断链表是否结束
            fast = fast.next;
            if(fast == null) break;
            else {
                i++;
                fast = fast.next;
            }
            if(fast == null) break;

            // slow后移一个
            slow = slow.next;
        }

        // 区分链表的长度
        if(i % 2 == 0){// 偶数
            slow = slow.next;
            while(slow != null){
                if(slow.val != stack.pop()){
                    return false;
                }
                slow = slow.next;
            }
        }else{// 奇数
            i = stack.pop();
            slow = slow.next;
            while(slow != null){
                if(slow.val != stack.pop()){
                    return false;
                }
                slow = slow.next;
            }
        }
        return true;
    }
}

思路描述

  • 没说不能破坏原有链表,那么还是快慢指针,等到快指针移动到最后时,区分链表长度的是技术还是偶数,将后半部分逆置。
  • 然后遍历前半部分和被逆置的后半部分链表,查看是否相同。
  • 时空间复杂度O(n)和O(1)

代码实现
java代码:

class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;

        // 
        ListNode fast = head.next, slow = head, temp;
        ListNode newhead = new ListNode(-1, null);
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }

        //开始逆置后续链表
        if(fast == null){// 链表长度为奇数
            while(slow != null){
                temp = slow.next;
                slow.next = newhead.next;
                newhead.next = slow;
                slow = temp;
            }
        }else{// 链表长度为偶数
            slow = slow.next;
            while(slow != null){
                temp = slow.next;
                slow.next = newhead.next;
                newhead.next = slow;
                slow = temp;
            }
        }

        // 判断元素是否相同
        while(newhead.next != null){
            if(head.val == newhead.next.val){
                
            }else{
                return false;
            }

            head = head.next;
            newhead = newhead.next;
        }

        return true;
    }
}

注意事项

  • 1.快指针和慢指针的位置关系。

拓展延伸

  • 快慢指针之间的距离关系,标准写法。
  • 比如说想要快慢指针位置关系为1:2
        ListNode fast = head.next, slow = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
  • 1:3
        ListNode fast = head.next.next, slow = head;
        while(fast != null && fast.next != null && fast.next.next != null){
            slow = slow.next;
            fast = fast.next.next.next;
        }

9 725. 分隔链表

题目

思路描述

  • 1.首先获取链表的长度,然后判断链表够不够长。也就是说len是否大于k,如果不大于则不够长,结果中肯定有空链表。而且各个非空链表肯定长度为1。
  • 2.如果够长则需要考虑怎么将链表进行划分,才能满足题目要求。靠前的链表长而且只会比后面的列表长1个单位。所以首先将链表均分,然后剩下的长度,从头依次向后添加给每个需要划分的小链链表,直到用光。

代码实现
java代码:

class Solution {
    public ListNode[] splitListToParts(ListNode head, int k) {
        // 预处理
        if(k == 1) {
            ListNode[] array = new ListNode[k];
            array[0] = head;
            return array;
        }

        // 获取链表的长度
        ListNode h = head;
        int len = 0;
        while(h != null){
            len++;
            h = h.next;
        }

        // 分割后链表最佳长度
        int lenSplited = len / k;
        // 链表平分后剩余数量
        int mod = len % k;
        
        int[] lensSplited = new int[k];
        for(int i =0; i < k; i++){
            lensSplited[i] = 0;
        }
        int i = 0;
        // 判断链表是否足够长,用于划分
        if(k > len){// 不够长
            while(i < len){
                lensSplited[i] = lenSplited; 
                i++;
            }
            return split(head, len, lensSplited, k);
        }else{// 够长
            while(i < k){
                lensSplited[i] = lenSplited + ((mod-- > 0)? 1 : 0);
                i++;
            }
            return split(head, len, lensSplited, k);
        }
    }

    // 分割链表为指定数量指定长度
    private ListNode[] split(ListNode head, int len, int[] lensSplited, int k){
        ListNode[] array = new ListNode[k];
        ListNode temp;
        // 初始值设为null
        for(int i = 0; i < k; i++){
            array[i] = null;
        }

        int listNum = 0;
        while(listNum < k && head != null){
            array[listNum] = head;
            int l = 1;
            while(l < lensSplited[listNum]){
                head = head.next;
                l++;
            }
            if(head != null){
                temp = head.next;
                head.next = null;
                head = temp;
            }
            listNum++;
        }
        return array;
    }
}

注意事项

  • 1.java不能对数组整体赋值吗?必须要用循环?

拓展延伸

  • 注意分析题目中的数学关系。

这个更简洁。

class Solution {
    public ListNode[] splitListToParts(ListNode head, int k) {
    int N = 0;
    ListNode cur = head;
    while (cur != null) {
        N++;
        cur = cur.next;
    }
    int mod = N % k;
    int size = N / k;
    ListNode[] ret = new ListNode[k];
    cur = head;
    for (int i = 0; cur != null && i < k; i++) {
        ret[i] = cur;
        int curSize = size + (mod-- > 0 ? 1 : 0);
        for (int j = 0; j < curSize - 1; j++) {
            cur = cur.next;
        }
        ListNode next = cur.next;
        cur.next = null;
        cur = next;
    }
    return ret;
    }
}

10 328. 奇偶链表

题目

思路描述

  • 这有什么难的?计数,区分奇偶,尾插法。
  • 不好意思,题理解错了。
  • 双指针,一个指奇节点一个指偶节点,新建两个头指针作为划分后的奇偶链表的头节点。

代码实现
java代码:

class Solution {
    public ListNode oddEvenList(ListNode head) {
        // 预处理
        if(head == null || head.next == null || head.next.next == null){
            return head;
        }

        // 头结点
        ListNode oddHead = head, evenHead = head.next;
        ListNode oddNode = evenHead.next, evenNode = oddNode.next, oddTail = oddHead, evenTail = evenHead;

        // 遍历区分奇偶节点
        while(oddNode != null && evenNode != null){
            // 尾插法奇链表
            oddTail.next = oddNode;
            oddTail = oddNode;
            oddNode = oddNode.next;
            if(oddNode != null){
                oddNode = oddNode.next;
            }else{
                break;
            }

            // 尾插法偶链表
            evenTail.next = evenNode;
            evenTail = evenNode;
            evenNode = evenNode.next;
            if(evenNode != null){
                evenNode = evenNode.next;
            }else{
                break;
            }
        }

        // 偶链表链到奇链表后
        if(oddNode == null){
            oddTail.next = evenHead;
            evenTail.next = null;
        }else{
            oddTail.next = oddNode;
            oddTail = oddNode;
            oddTail.next = evenHead;
            evenTail.next = null;
        }

        return oddHead;
    }
}

java代码:

class Solution {
    public ListNode oddEvenList(ListNode head) {
        // 预处理
        if(head == null || head.next == null || head.next.next == null){
            return head;
        }

        // 
        ListNode evenHead = head.next, oddNode = head, evenNode = head.next;
        while(evenNode != null && evenNode.next != null){
            oddNode.next = oddNode.next.next;
            oddNode = oddNode.next;

            evenNode.next = evenNode.next.next;
            evenNode = evenNode.next;
        }

        // 偶链表接到奇链表后面
        oddNode.next = evenHead;
        return head;
    }
}

注意事项

  • 注意事项

拓展延伸

  • 拓展延伸

11 剑指 Offer 35. 复杂链表的复制

题目

思路描述

  1. 都在注释里了,我的方法很暴力.
  2. 虽然random指向是随机的,但是指向的节点在链表中的位置是一定的,所以我们可以通过得到每个节点的序号,来找到每个节点对应的random指向。
  3. 我现在的方法是遍历链表找序号对应的节点,有没有什么办法能够将遍历的过程省去呢?得到一个节点直接能够知道他的位置。
  4. 时空复杂度:O(n2) O(1)

代码实现
java代码:

class Solution {
    public Node copyRandomList(Node head) {
        // 先按照普通的链表进行复制,random指针先赋值为null
        Node newH = new Node(-1001); // 头指针尾指针复制链表
        Node tail = new Node(-1001);
        Node originalHead = head;
        newH = tail;
        int len = 0; // 获取链表的长度
        while (head != null) { // 仅复制next节点
            Node tmp = new Node(-1001);
            tmp.val = head.val;
            tmp.next = head.next;
            // tail.random = null; // 初始化的tail的random本身就是空

            head = head.next;
            tail.next = tmp;
            tail = tmp;
            len++;
        }
        tail.next = null; // 尾节点置空

        head = originalHead; // 原始链表的头,用于遍历
        Node copyListHead = newH.next; // 复制链表的头
        Node nowProcess = copyListHead; // 当前正在处理的复制链表中的节点
        Node tmpHead = new Node(-1001); // 复制链表的头,用于遍历复制链表,寻找对应的random节点
        int copyIdx;
        while (head != null) {
            // 首先找到当前head节点指向的random节点是链表中的倒数第几个
            // 倒数第零个表示null
            int idx= 0; // 计数
            Node randNode = head.random;
            while (randNode != null) {
                idx++; 
                randNode = randNode.next;
            }

            // 同样找到在copyList中对应的倒数那个
            
            // 这里可以优化一下
            copyIdx = len;
            tmpHead = copyListHead;
            while (copyIdx != idx) {
                tmpHead = tmpHead.next;
                copyIdx--;
            }
            nowProcess.random = tmpHead;

            head = head.next;
            nowProcess = nowProcess.next;
        }

        return newH.next;
    }
}

思路描述

  1. 将原始链表进行拆分复制,接在原始链表中每一个对应节点的后面。
    在这里插入图片描述
    在这里插入图片描述
  2. 根据原始节点的random指向,获取到新复制出的节点,并修改复制出的节点的random指针。
    在这里插入图片描述
  3. 恢复原始链表的next指向,修改复制链表的next指向。
    在这里插入图片描述
  4. 时空复杂度:O(3n) O(1)
  5. 必须要三个循环才可以,官方题解说的“读者们也可以自行尝试在计算拷贝节点的随机指针的同时计算其后继指针,这样只需要遍历两次。”,即使像这样做了,还是要一个循环来复原原始链表的next指向,如果修改原始next指针和修改拷贝节点的next指针同时进行的话,可能会导致拷贝节点的random指针指向了原始节点中的值,这样就不符合题意了。
class Solution {
    public Node copyRandomList(Node head) {
        // 1 -> 2 -> 3 -> 4
        // 1 -> 1 -> 2 -> 2 -> 3 -> 3 -> 4 -> 4
        if (head == null) return head; // 特殊情况

        Node node = head;
        while (node != null) { // 将原始链表复制一份,并穿插在原始链表中,每两个相同的相邻接点中第二个是新节点,未来用于返回
            Node newNode = new Node(node.val); // 新建节点
            newNode.next = node.next; // 复制next关系
            node.next = newNode; // 接在原始节点后面
            node = newNode.next; // 遍历
        }
        
        Node tmpHead = head;
        while (tmpHead != null) {
            node = tmpHead.next;
            node.random = tmpHead.random == null ? null : tmpHead.random.next; // 查找random节点,并赋值给copy出来的链表
            tmpHead = tmpHead.next.next;
        }

        // 将原始链表的next关系复原,并修改所复制链表的next关系。
        tmpHead = head;
        Node copyHead = head.next;
        while (tmpHead != null) {
            node = tmpHead.next;
            tmpHead.next = tmpHead.next.next; // 原始链表next复原
            node.next = node.next == null ? null : node.next.next; // 复制链表的next关系复原
            tmpHead = tmpHead.next; // 遍历
        }

        return copyHead;
    }
}

注意事项

  1. 注意事项

拓展延伸

  1. 拓展延伸

12 25. K 个一组翻转链表

题目

思路描述

  1. 题不难,但是有好多小细节,而且自己的思路有点不完善,导致最终写出的代码有点ugly。
  2. 时空复杂度:O(n) O(1)

代码实现

class Solution {
    int len;
    
    public ListNode reverseKGroup(ListNode head, int k) {
        // 统计长度 + 翻转链表
        if (head.next == null) return head; // 特殊情况

        ListNode h = new ListNode();
        ListNode tail = new ListNode();
        h.next = null;
        tail = null;

        ListNode[] headTail = new ListNode[2];
        ListNode[] twoHead = new ListNode[2];
        do {
            len = k;
            twoHead = countLength(head);
            headTail = reverseLinkList(twoHead[0], twoHead[1]);

            if (h.next == null) {
                h.next = headTail[0];
            }
            if (tail == null) {
                tail = headTail[1];
            } else {
                tail.next = headTail[0];
                tail = headTail[1];
            }

            head = twoHead[1];
        } while(head != null);

        return h.next;
    }

    private ListNode[] countLength(ListNode head) {
        ListNode[] twoHead = new ListNode[2];
        twoHead[0] = head;
        while (len-- != 0 && head != null) {
            head = head.next;
        }
        twoHead[1] = head;

        return twoHead;
    }

    private ListNode[] reverseLinkList(ListNode head, ListNode nextHead) {
        if (nextHead == null && len != -1) { // 剩余长度不够的情况
            return new ListNode[]{head, null};
        }
        // 翻转链表
        ListNode h = new ListNode();
        ListNode tail = new ListNode();
        h.next = null;
        tail = null;
        ListNode tmp = new ListNode();
        while (head != nextHead) {
            tmp = head;
            head = head.next;
            tmp.next = h.next;
            h.next = tmp;

            if (tail == null) {
                tail = tmp;
            }
        }
        tail.next = nextHead;

        return new ListNode[]{h.next, tail};
    }
}

注意事项

  1. 注意事项

拓展延伸

  1. 官方写的就很不错
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode hair = new ListNode(0); // 指向head的“头发”结点,头之前是头发23333
        hair.next = head;
        ListNode pre = hair;

        while (head != null) {
            ListNode tail = pre;
            // 查看剩余部分长度是否大于等于 k
            for (int i = 0; i < k; ++i) {
                tail = tail.next;
                if (tail == null) { // 如果此时剩下的节点个数不够k个,那么就可以直接返回了
                    return hair.next;
                }
            }
            ListNode nex = tail.next; // 找到了要反转的这一段的尾节点,并且求出下一段的头节点
            ListNode[] reverse = myReverse(head, tail); // 将目前head 到 tail这一段进行反转
            head = reverse[0]; // 反转后的头
            tail = reverse[1]; // 反转后的尾
            // 把子链表重新接回原链表
            pre.next = head;
            tail.next = nex;

            // 更新头发和头指针,进行下一段的反转工作
            pre = tail;
            head = nex;
        }

        return hair.next;
    }

    public ListNode[] myReverse(ListNode head, ListNode tail) { // 反转具体的一段链表
        ListNode prev = tail.next; // 先求出到哪里终止反转,tail的next
        ListNode p = head; // 
        while (prev != tail) { // 这个反转的方式有点妙啊
            ListNode nex = p.next; // 
            p.next = prev; // 
            prev = p;
            p = nex;
        }
        
        return new ListNode[]{tail, head};
    }
}

13 92. 反转链表 II

题目

思路描述

  1. 时空复杂度:O(n) O(1)

代码实现

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        if (head == null || head.next == null) {
            return head;
        }
        if (left >= right) {
            return head;
        }

        ListNode h = new ListNode();
        h.next = head;
        ListNode lTail, mHead, mTail, rHead;
        mHead = h;
        while (left != 1) {
            mHead = mHead.next;
            left--;
            right--;
        }
        lTail = mHead; // 被截断后的左侧链表的尾节点
        mHead = mHead.next; // 中间这一段的head
        mTail = mHead;
        while (right != 1) {
            mTail = mTail.next;
            right--;
        }
        rHead = mTail.next; // 被截断后的右侧链表的head
        mTail.next = null; // 截断中间这一段链表
        
        ListNode[] headAndTail = new ListNode[2];
        headAndTail = reverse(mHead); // 反转中间链表
        lTail.next = headAndTail[0];
        headAndTail[1].next = rHead;

        return h.next;
    }

    private ListNode[] reverse(ListNode head) { // 反转链表
        ListNode h = new ListNode();
        h.next = null;
        ListNode tmp, tail = head;
        while (head != null) {
            tmp = head;
            head = head.next;
            tmp.next = h.next;
            h.next = tmp;
        }

        return new ListNode[]{h.next, tail};
    }
}

注意事项

  1. 注意事项

拓展延伸

  1. 官

14 143. 重排链表

题目

思路描述

  1. 时空复杂度:O(n) O(1)

代码实现

// 链表取中 + 反转 + 交错链接
class Solution {
    public void reorderList(ListNode head) {
        // 先找中间,分成两段
        // 后面反转
        // 交错链接
        if (head == null || head.next == null) {
            return;
        }

        ListNode fast = head.next, slow = head; // 注意快指针一定要先后置一个
        while (fast != null && fast.next != null) { // 链表取中一定要这样写!!!
            slow = slow.next;
            fast = fast.next.next;
        }

        ListNode head2 = slow.next;
        slow.next = null;

        head2 = reverse(head2);
        ListNode tmp1;
        while (head2 != null) {
            tmp1 = head;
            head = head.next;
            tmp1.next = head2;

            tmp1 = head2;
            head2 = head2.next;
            tmp1.next = head;
        }

        return;
    }

    private ListNode reverse(ListNode head) {
        ListNode hair = new ListNode();
        hair.next = null; // 今天链表反转都写错了
        ListNode tmp;
        while (head != null) {
            tmp = head;
            head = head.next;
            tmp.next = hair.next;
            hair.next = tmp;
        }

        return hair.next;
    }
}

注意事项

  1. 链表取中、取三分之一时,的确使用快慢指针,但是一定要提前判断在进入这次循环前,快指针还能不能走完下一次快走的步数
		// 取中
        ListNode fast = head.next, slow = head; // 注意快指针一定要先后置一个
        while (fast != null && fast.next != null) { // 链表取中一定要这样写!!!
            slow = slow.next;
            fast = fast.next.next;
        }
        // 取三分之一
        ListNode fast = head.next, slow = head; // 注意快指针一定要先后置一个
        while (fast != null && fast.next != null && fast.next.next != null) { // 一定要这样写!!!
            slow = slow.next;
            fast = fast.next.next.next;
        }

拓展延伸

  1. 官

15 23. 合并K个升序链表

题目

思路描述

  • 时空复杂度O(knlogk), O(logk)
    在这里插入图片描述

代码实现

// 归并 时空复杂度O(knlogk), O(logk)
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        int n = lists.length;
        if (n == 0) return null;
        if (n == 1) return lists[0]; // 特殊情况处理

        return merge(lists, 0, n - 1); // 划分子问题
    }

    private ListNode merge(ListNode[] lists, int l, int r) { // 切分子问题
        if (l > r) {
            return null; // 如果当前没有子问题可以切分,直接返回null
        }
        if (l == r) {
            return lists[l]; // 如果当前的区间中只有一个链表,直接返回这个链表
        }

        int mid = l + ((r - l) >> 1); // 区间中有多个链表,我们取中平分
        ListNode l1 = merge(lists, l, mid); // 递归的划分子问题
        ListNode l2 = merge(lists, mid + 1, r);

        return mergeSort(l1, l2); // 传统的归并过程
    }

    private ListNode mergeSort(ListNode l1, ListNode l2) {
        if (l1 == null || l2 == null) {
            return l1 == null ? l2 : l1;
        }

        ListNode hair = new ListNode();
        ListNode tail = hair;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                tail.next = l1;
                l1 = l1.next;
            } else {
                tail.next = l2;
                l2 = l2.next;
            }
            tail = tail.next;
        }

        tail.next = l1 == null ? l2 : l1;

        return hair.next;
    }
}

在这里插入图片描述

// 优先队列,小顶堆 时空复杂度O(knlogk), O(k) 这里用了优先队列,优先队列中的元素不超过 k 个,故渐进空间复杂度为 O(k)。
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        int n = lists.length;
        if (n == 0) return null;
        
        PriorityQueue<ListNode> pq = new PriorityQueue<>(n, (a, b) -> (a.val - b.val)); // 自定义比较器
        for (int i = 0; i < n; i++) { // 第一次放入
            if (lists[i] != null) {
                pq.offer(lists[i]);
            }
        }
        ListNode hair = new ListNode();
        ListNode tail = hair;
        ListNode tmp;
        while (!pq.isEmpty()) {
            tmp = pq.poll();
            tail.next = tmp;
            tail = tail.next;
            if (tmp.next != null) {
                pq.offer(tmp.next);
            }
        }

        return hair.next;
    }
}

注意事项

  1. 链

拓展延伸

  1. 官

16 148. 排序链表

题目

思路描述

  1. 时空复杂度:O(nlogn) O(logn),基于数组的空间复杂度是O(n),但是这道题是链表,不需要额外的空间,是需要开辟递归栈,所以空间复杂度是O(logn).

代码实现

// 归并 时空复杂度:O(nlogn) O(logn)
class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) // 如果当前是空或者只有一个节点
            return head;

        ListNode fast = head.next, slow = head;
        while (fast != null && fast.next != null) { // 分割子问题
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode tmp = slow.next; // tmp右侧链表的头
        slow.next = null; // 分割链表

        ListNode left = sortList(head); // 左右两个区间
        ListNode right = sortList(tmp);

        ListNode hair = new ListNode(0); // 合并后的头
        ListNode tail = hair; // 尾插法
        while (left != null && right != null) { // 归并
            if (left.val < right.val) {
                tail.next = left;
                left = left.next;
            } else {
                tail.next = right;
                right = right.next;
            }
            tail = tail.next;
        }
        tail.next = left != null ? left : right;

        return hair.next;
    }
}
// 快排 时空复杂度O(nlogn)O(logn)
class Solution {
    public ListNode sortList(ListNode head) {
        return quickSort(head);
    }
    
    public ListNode quickSort(ListNode head) {
        if(head == null || head.next == null) return head; // 如果问题规模过小,直接返回

        ListNode slow = head, fast = head.next; // 快慢指针取中间节点
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        int val = slow.val; // 基准值

        ListNode h1 = new ListNode(); // 基准值左侧的链表头
        ListNode h2 = new ListNode(); // 基准值右侧的链表头
        ListNode h3 = new ListNode(); // 可能会有和基准值相同的节点,我们把他全部保留在这个头结点后面,这样我们可以减少接下来的子问题的规模
        ListNode t1 = h1, t2 = h2; // 基准值左侧和右侧的链表的尾指针,因为我们是要使用尾插法保证顺序
        ListNode t3 = h3; // 所有的基准值,为了保持一致,我们一样用尾插法构造链表
        ListNode cur = head; // 当前遍历到的元素
        
        while(cur != null) {
            ListNode next = cur.next; // 暂存下一个元素
            if(cur.val < val) { // 如果当前这个元素的大小小于基准值
                cur.next = t1.next; // 使用尾插法将当前这个节点链到基准值左侧的链表上
                t1.next = cur;
                t1 = t1.next;
            } else if(cur.val > val) { // 如果当前这个元素的大小大于基准值
                cur.next = t2.next; // 使用尾插法将当前这个节点链到基准值右侧的链表上
                t2.next = cur;
                t2 = t2.next;
            } else { // 相等的部分,留在这里,等着链接
                cur.next = t3.next;
                t3.next = cur;
                t3 = t3.next;
            }
            cur = next; // 遍历下一个元素
        }

        h1 = quickSort(h1.next); // 递归左侧链表,注意这里的h1已经不是原来的h1了。
        h2 = quickSort(h2.next); // 递归右侧链表

        h3 = h3.next;
        t3.next = h2; // 基准值链表链接右侧较大部分链表
        if(h1 == null) { // 如果左侧链表为空,直接返回基准值以及基准值之后的元素
            return h3;
        } else { // 否则,我们需要找到左侧传回链表的尾部,因为原来左侧只是经过了划分,而没有排序,排完序后我们需要重新寻找它的尾节点
            t1 = h1; // 寻找尾节点
            while(t1.next != null) {
                t1 = t1.next;
            }
            t1.next = h3; // 链接上基准值和基准值右侧的节点
            return h1;
        }
    }
}

注意事项

  1. 链

拓展延伸

  1. 官

17 146. LRU 缓存

题目

思路描述

  1. 时空复杂度:

代码实现

public class LRUCache {
    // 定义内部类,
    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
        public DLinkedNode() {}
        public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
    }

    // 定义成员变量
    private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>(); // hashmap用于get
    private int size; // 当前大小
    private int capacity; // 容量上限
    private DLinkedNode head, tail; // 双向链表的头尾指针

    public LRUCache(int capacity) { // 初始化
        this.size = 0; // 初始容量为零
        this.capacity = capacity; // 容量上限

        // 使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail; // 初始化指向
        tail.prev = head;
    }

    public int get(int key) { // get 方法
        DLinkedNode node = cache.get(key); // 先获取这个节点
        if (node == null) { // 不存在的话返回 -1
            return -1;
        }
        moveToHead(node); // 如果 key 存在,先通过哈希表定位,再移到头部
        
        return node.value;
    }

    public void put(int key, int value) {
        DLinkedNode node = cache.get(key); // 通过hashmap获取对应节点
        if (node == null) {
            DLinkedNode newNode = new DLinkedNode(key, value); // 如果 key 不存在,创建一个新的节点
            cache.put(key, newNode); // 添加进哈希表
            addToHead(newNode); // 添加一个新节点至双向链表的头部
            ++size; // 当前容量++
            if (size > capacity) { // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode tail = removeTail();
                cache.remove(tail.key); // 删除哈希表中对应的项
                --size; // 容量--
            }
        } else {
            node.value = value; // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            moveToHead(node); // 移动旧有节点到队头
        }
    }

    // 双向链表的操作
    private void addToHead(DLinkedNode node) { // 新添加进来的节点放到队头
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    private void removeNode(DLinkedNode node) { // 删除一个节点
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private void moveToHead(DLinkedNode node) { // 移动一个已存在的节点到队头,相当于把这个节点从原来的双向链表中删除,再新建一个节点到队头
        removeNode(node);
        addToHead(node);
    }

    private DLinkedNode removeTail() { // 删除最久没有使用的节点
        DLinkedNode res = tail.prev; // 先获取队尾的节点
        removeNode(res); // 删除这个节点
        return res; // 返回被删除的节点,用于将hashmap中的节点一并删除
    }
}

注意事项

  1. 链

拓展延伸

  1. 官
Ubuntu22.04+Docker一键部署Autoware Universe避坑指南(附鱼香ROS配置) 本文详细介绍了在Ubuntu22.04系统中使用Docker一键部署Autoware Universe的完整流程,包括鱼香ROS工具链配置、Docker环境优化及VSCode开发环境集成。通过实战指南帮助开发者快速搭建自动驾驶开发环境,解决常见部署问题,提升开发效率。 阅读详情

相关推荐

主动式桥梁防船撞预警系统的方案汇总

本系统以精密光电测高前端(2台,上下游各1台)、固态雷达(2台,上下游各1台)、AIS(1台)、偏航辅助前端(4台,上下游各2台)为核心,通过人工智能,对画面上的船舶目标进行深度学习、精确定位,再用系统集成方法将各设备有机结合在一起,构建成套的桥梁防撞主动预警系统,全天候监控过往船舶。主动式桥梁防船撞预警系统主要船舶监测采集单元、控制单元、船舶超高、偏航信息检测单元、以及视频监控单元组成,,通过声光警示、短信文字、船舶身份识别及海事对讲频道、视频录像取证等多种手段与船舶进行信息互动,主动防止船舶碰撞桥梁!

gdxixun的博客 970

东华大学2020考研计算机复试准备上机题解析答案_进阶篇(31-60)

文章目录前言31 最高频率 前言 提交代码: 选择C++编程语言,因为有的时候会用到C++的一些方便的头文件什么的,还有我编写代码是有一部分是纯C的,因为做题来讲C的scanf和printf很方便。 发布文章安排: 我会抽时间发文章的,看时间安排了,现在时间有点紧吧。马上过年了。过完年要开始准备准备其他东西了 解题解法质量: 关于我的解法和代码的精简程度,我是以当时做题的心态来解题的,由于当时急着...

zhou_hao_ran的博客 5392

Halcon: 获取仿射矩形-rectangle2的四个顶点、四条边的中点

Halcon 工业应用实用教程中的一个例子: * CenterY : 仿射矩形中心y * CenterX: 仿射矩形中心x * Phi: 仿射矩形角度 * Len1: 仿射矩形宽的一半 * Len2: 仿射矩形高的一半 * CornerY: 仿射矩形端点y * CornerX: 仿射矩形端点x * LineCenterY: 仿射矩形边中点y * LineCenterx: 仿射矩形边中点x * 矩...

ruotianxia的博客 8572

【Leetcode学习笔记】链表

【Leetcode学习笔记】链表

weixin_42685184的博客 564

爬虫实战:拆解WASM加密参数,破局2026电商动态接口反爬(实测某头部电商可用)

sign和wasm_token,而且每次请求这两个参数都在动态变化。用常规的参数拼接、MD5加密尝试还原,结果全是403;反编译前端JS,发现核心加密逻辑被打包成了.wasm,把关键参数加密、签名逻辑藏在二进制模块里,让爬虫无法直接逆向JS拿到加密规则。前后折腾了5天,从WASM文件解析、JS与WASM交互逻辑拆解,到本地还原加密参数、实战验证,踩了无数坑(比如WASM版本不兼容、内存数据读取失败、加密依赖环境缺失),最终成功还原了加密流程,稳定爬取商品详情、价格、销量等核心数据,连续运行72小时无封禁。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1913

VN1640 CH5 I/O通道终极指南:【从硬件原理—>工程实战—>自动化测试实战】

摘要:本文详细解析Vector VN1640 CH5 I/O通道的硬件原理与工程应用,涵盖模拟输入(AIN)、数字输入(DIN)和数字输出(DOUT)三大功能模块的配置指南。通过硬件隔离设计实现高精度信号采集与驱动控制,重点分析开漏输出特性及保护机制。提供PWM控制、ECU上电时序测试、传感器闭环控制等实战案例的CAPL脚本代码,并强调多设备同步采集的精准性(时间偏差<1µs)。适用于汽车电子HIL测试、ECU信号监测等场景,所有方案均通过实际项目验证。

汽车控制器系统开发、VIL测试、HIL测试、MIL测试、EMS电喷系统标定与开发、热管理系统标定 759

Leetcode——链表

Leetcode——链表合并两个有序链表删除排序链表中的重复元素环形链表相交链表移除链表元素反转链表回文链表删除链表中的节点 链表(Linked List)是最简单的线性的、动态数据结构。理解它是理解树结构、图结构的基础。 区别于数组,链表中的元素不是存储在内存中连续的一片区域,链表中的数据存储在每一个称之为「结点」复合区域里,在每一个结点除了存储数据以外,还保存了到下一个节点的指针(Pointer)。 由于不必按顺序存储,链表在插入数据的时候可以达到 O(1)O(1) 的复杂度,但是查找一个节点或者访问特

weixin_48367136的博客 1464

Claude Code 完整详解(Anthropic 官方 AI 编程 Agent)

它是运行在终端(Terminal)的 AI 编程代理,不是普通 IDE 插件;你用自然语言发指令,它自主规划步骤、读写项目文件、执行系统命令、排查 Bug、处理 Git 工作流,是能真正动手干活的 AI 程序员。真正的AI 开发代理人,不止写代码,还能执行开发全流程超长全局上下文,大型项目理解碾压传统补全工具终端原生 + IDE 双接入,开发流程无割裂代码质量、可读性、工程规范性普遍偏高权限安全机制完善,修改前预览、可随时中断回滚。

wange6906的博客 3155

Leetcode题解——数据结构之链表

1. 找出两个链表的交点 2. 链表反转 3. 归并两个有序的链表 4. 从有序链表中删除重复节点 5. 删除链表的倒数第 n 个节点 6. 交换链表中的相邻结点 7. 链表求和 8. 回文链表 9. 分隔链表 10. 链表元素按奇偶聚集 链表是空节点,或者有一个值和一个指向下一个链表的指针,因此很多链表问题可以用递归来处理。 1. 找出两个链表的交点 1...

dieshi8689的博客 812

23、微波电路中的过孔、过孔围栏与接地焊盘建模分析

本文详细分析了微波电路设计中过孔、过孔围栏及接地焊盘的建模与优化方法。探讨了过孔模型的发展及其电感与电容特性,通过场求解器和集总元件模型提高建模精度。此外,分析了过孔隔离围栏在改善信号隔离中的作用,并通过2.5D和3D电磁仿真验证其效果。最后,研究了接地焊盘中过孔布局对电路稳定性的影响,提出了优化过孔位置以消除谐振和杂散振荡的方法。文章为微波电路设计提供了重要的理论支持和实践指导。

convnet3designer的博客 164

rk3568 Android12 默认授予预装app权限

默认授予预装应用权限可以避免用户在每次使用应用时都要重复同意权限请求。尽管在Android 12中,用户对权限的管理有了更高的控制权,但这也可能导致用户在频繁使用预装应用时感到繁琐和不必要的干扰。通过默认授权,减少了这种麻烦。一些厂商可能会根据其定制的Android系统(如Samsung的One UI、Xiaomi的MIUI)要求某些预装应用默认获得权限,以便应用能够提供厂商特定的功能。这也有助于设备厂商提供统一和稳定的服务,不至于因为权限问题导致某些功能无法使用。

weixin_35723192的博客 661

LeetCode中的数据结构2:链表

链表问题要尤其注意,检查边界条件是否考虑全面,代码在边界条件下是否能正确运行,如:链表为空、链表只包含一个结点、链表只包含两个结点、代码逻辑处理头结点和尾结点时,能否正常执行。

铁马秋风大散关 288

MATLAB 基于深度学习的步态识别算法仿真

步态识别是一种通过分析人体行走方式来识别身份的技术。基于深度学习的步态识别算法利用卷积神经网络 (CNN) 提取步态特征,并通过分类器进行身份识别。基于深度学习的步态识别算法是实现身份识别的重要手段,具有广泛的应用前景。MATLAB 提供了丰富的工具和函数,可以方便地进行算法仿真和性能评估。

走向CTO的路上... 518

数据结构——线性表(链表,力扣中等篇,技巧型)

当链表无序时,去重操作无法像有序链表那样通过简单比较相邻节点来完成,需要借助额外的数据结构来记录已出现过的值。因为链表是排序的,重复元素一定相邻,所以只需比较当前节点和下一个节点的值。思路就是创建两个临时链表,分别存储小于 x 的节点和大于或等于 x 的节点,最后将这两个链表连接起来,保持原有节点的相对顺序。思路2:先将链表连成环,再找到新的头节点位置并断开环,从而完成旋转操作。思路1:将链表分成两个小链表,然后将两个链表交换位置,重新连接即可。不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

2303_77489296的博客 2338

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Leetcode刷题记录——链表篇

本文是一些leetcode链表刷题笔记,都是暴力方法,还未优化,为了快速能够先通过题目,之后再考虑如何A,希望能给大家带来点启发。

shineber的博客 2172

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

LeetCode——链表篇

LeetCode——链表篇一、移除链表元素题解代码二、反转链表题解代码三、两两交换链表中的节点题解代码四、删除链表的倒数第 N 个结点题解代码五、相交链表题解代码六、环形链表题解代码七、环形链表 ||题解代码 一、移除链表元素 题解 该题其实就只需要遍历一下链表,需要注意的是,做这种题目的时候,最好新建一个头结点,否则在执行删除操作的时候难以进行。删除操作是链表中最基本的操作,需要熟练掌握。 代码 # Definition for singly-linked list. # class ListNode:

Pcl2001的博客 1894

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

力扣刷题记录

Leecode

Mr_Liuss的博客 2636

[每日一练] 移除链表元素 - 力扣(LeetCode)

​ 🍹欢迎各路大佬来到 Nick 主页指点 ☀️本期文章将学习 [每日一练] 移除链表元素 - 力扣(LeetCode),我是博主Nick。✨ ✨我的博客主页:Nick_Bears 🌹꧔ꦿ 🌹꧔ꦿ博文内容如对您有所帮助,还请给个点赞 + 关注 + 收藏✨ ​

Nick 的笔记 798

【反转链表系列】力扣206,92,25

本文涉及的力扣题目,涉及简单、中等、困难三个难度力扣206.反转链表力扣92.反转链表 II力扣25.K 个一组翻转链表反转链表这类型的题目,折磨了挺久,经常弄懂了一道题,但是遇到下一道反转链表的题目还是不会。所以我整理了三道比较经典的反转链表的题目,从浅到深一步一步领悟反转链表的处理思路。

沉迷写代码的程序猿的博客 1147

408系列二:数据结构&&力扣必做题

接上篇博客记录的408整体介绍,从这一篇博客开始,我将详细介绍408每个科目的具体备考策略。初步介绍一些关于数据结构的基础知识,主要介绍的是算法方面,希望可以帮助你~

weixin_73995703的博客 1193

力扣刷题记录:

int val;// 节点上存储的元素// 指向下一个节点的指针ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数//结点的值int val;//下一个结点//无参构造函数//节点的构造函数//节点的构造函数什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。203. 移除链表元素。

2504_91017195的博客 1517

力扣OJ题感悟——链表篇

1.移除链表元素 代码如下: /** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* removeElements(struct ListNode* head, int val) ...

New_learner55的博客 470

数据结构之线性表——LeetCode:707. 设计链表,206. 反转链表,92. 反转链表 II

模拟了一个链表数据结构,提供了初始化链表、获取特定位置节点值、在头部插入节点、在尾部插入节点、在特定位置插入节点以及删除特定位置节点等功能。设置为 0,表示链表中没有实际节点,同时创建一个虚拟头节点。次(因为虚拟头节点不算实际节点),找到目标节点并返回其值。以指示链表中的上一个节点。,即反转当前节点的指针方向,使其指向前一个节点。,即将当前节点变为反转后的链表中的新的末尾节点。的下一个节点,保存链表的后续部分。,实现在链表尾部插入节点的功能,因为当在长度为。的位置插入节点时,相当于在链表末尾追加节点。

u014114223的博客 932

【数据结构与算法 | 灵神题单 | 遍历链表篇】力扣1290, 2058

用栈来记录链表的元素,由于栈的性质,最先弹栈的元素是末位,转换成10进制需要*1,其次弹栈的元素转换成10进制需要*2...直到栈空,将所有的值加起来返回。,2,2,3,2,2,2,7]:第二个节点是一个局部极大值点,因为 3 比 1 和 2 大。,2,5,1,2]:第三个节点是一个局部极小值点,因为 1 比 3 和 2 小。,2,2,2,7]:第五个节点是一个局部极大值点,因为 3 比 2 和 2 大。,1,2]:第五个节点是一个局部极大值点,因为 5 比 2 和 1 大。如果临界点少于两个,则返回。

2301_80912559的博客 975

【数据结构】(牛客)链表的回文结构,LeetCode相交链表,LeetCode环形链表

本文详细讲述了(牛客)链表的回文结构,LeetCode相交链表,LeetCode环形链表等题目,里面讲述了我自己的思路,并将做题思路转化成动图展现出来,有助于更好地理解。

m0_63198468的博客 1385

力扣---链表1

本博客用于力扣链表系列的刷题记录,跟的是代码随想录的顺序,在刷完之后,最后对链表相关的博客做规整和总结,同之前的数组。

2301_78138281的博客 749
上一篇: 目标检测算法
下一篇: Hadoop学习笔记
LencyGe
博客等级 码龄7年 26粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值