[数据结构复习]链表的基操

108 阅读4分钟

题目

分隔链表

链接:86. 分隔链表 - 力扣(LeetCode)

题目描述:

给你一个链表,再给你一个target,保证:所有小于target的节点都在大于等于target节点的前面

解题思路:

其实题目的意思就是:让你分别表示大于等于target的链表和小于target的链表,然后让两个链表结合起来,就可以完成。

思路很简单,写起来也很轻松,但是,可能你第一遍的时候做不对🙄(因为我做错了),原因其实很简单,就是因为p.next后面比较混乱,画个图举个例子

86分隔链表.png

其实我们只需要两个链表,也就是有一些黑色的线,我们不需要,这时候我们就需要断开线子

那么我们怎么才算是断开线呢?

p.next = null;

代码:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */
function partition(head: ListNode | null, x: number): ListNode | null {
    // 其实这题,也就是将一个链表分割为两个链表
    let dummy1 = new ListNode(-1);
    let dummy2 = new ListNode(-1);
    let small = dummy1;
    let large = dummy2;
    let p = head;
    while(p!== null){
        if(p.val >= x){
            large.next = p;
            // large向前进一位
            large = large.next
        }else{
            small.next = p;
            // small向前进一位
            small = small.next;
        }
        // p = p.next;
        // 断开原链表里面的next指针
        // 如果不断开连接,最终可能不是两个链表,就会出现指向混乱
        let temp  = p.next;
        // 让p的指针消失,其实就是避免产生三个柔和的链表的感觉
        p.next = null;
        p = temp;
    }
    // 现在将两个重合起来
    small.next = dummy2.next;
    return dummy1.next
};

反转链表

链接:206. 反转链表 - 力扣(LeetCode)

题目描述:

大概意思就是,给你一个链表的头节点,请你将链表反转过来

思路:

这边应该是很容易想到使用precur两个指针,只要我不断地将他们的next指向反过来,那么我不是就可以实现了嘛

反转链表.png

代码

function reverseList(head: ListNode | null): ListNode | null {
    // 将链表进行反转
    // 设置pre节点,next节点,和cur节点
    let pre = null
    let cur  = head
    while(cur!== null){
        // 为了一直向后进行
        let nxt = cur.next
        cur.next = pre;
        pre = cur
        cur = nxt
    }
    return pre
};

这边我们其实最主要的就是cur.next= pre,这就是将那个箭头扳过来,但是为什么我们需要其他的那三条语句呢?

因为我们需要继续向后面去反转啊,那么我们就需要让pre去占领cur的位置,让cur去占领nxt的位置,这样就可以一直反转到最后

反转链表时.png

移除链表元素

链接:203. 移除链表元素 - 力扣(LeetCode)

题目描述:

给你一个链表的头节点,并且给你一个target,希望你将链表里,所有等于target的值全部去掉

思路:

我们可以直接进行遍历,然后去寻找那个target值,当找到了,我们就直接跳过他

删除节点.png

代码:

function removeElements(head: ListNode | null, val: number): ListNode | null {
    let dummy = new ListNode(0,head);
    let data = dummy;
    while(data.next && data){
        if(data.next.val === val){
            //那么需要跳过这个节点
            // nextdata = nextdata.next
            data.next = data.next.next
            continue
        }
        data = data.next
    }
    return dummy.next
};

链表中的倒数第K个节点

链接:剑指 Offer 22. 链表中倒数第k个节点 - 力扣(LeetCode)

思路:

  1. 其实对于暴力解,我们应该都能想到

    • 倒数第K个节点,那么对于链表,我们希望得到正着数的第n-k个节点,那就意味着,我们需要知道,这个链表需要多少个节点,然后我们去找到第n-k+1个(题目说,是从下标一开始)
    • 也就是说,这么做,其实我们需要两个for循环,那么想想怎么使用一个for循环呢?
  2. 东哥提供了一种思路,非常巧妙

Offer22.png

  • 这样我们就可以不用求链表的长度,用两个指针,用相对位置求出倒数第k个节点

代码:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */
function getKthFromEnd(head: ListNode | null, k: number): ListNode | null {
    // 思路:
    // 首先找到第K个节点
    let p1 = head;
    let index = 0;
    while(index < k){
        p1 = p1.next;
        index++
    }
    let p2 = head;
    // 现在利用相对距离
    while(p1 !== null){
        p2 = p2.next;
        p1 = p1.next
    }
    return p2
};

参考链接:双指针技巧秒杀七道链表题目 :: labuladong的算法小抄 (gitee.io)

总结

今天看的这几道题,其实都不是很难,但是需要注意一些细节,并且需要巧妙地使用工具节点

第一题:我们需要避免产生指针指向混乱的情况,所以,需要将原链表的next指针

第二题:我们需要使用两个指针,然后再遍历的过程中,将他们的位置改变

第四题:双指针,但是用相对位置来求出答案,非常巧妙。

工具节点:我的理解就是,当你想对链表增删改查的时候,后面有需要输出链表啥的,你就可以设置dummyNode,感觉它就像一个工具节点一样。

额,我理解的工具节点就是那种:值和头节点值一样的那个节点(自己认为设置的)

let p1 = head;//p1,我就认为是工具节点