链表

首页 / 算法笔记 / 正文

链表这种数据结构的考察实在是太常见了,从大一的c语言课程设计就已经接触到了,作为一种动态的数据结构,它实在是有太多的应用了。话不多说,直接看题。

剑指Offer24

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

思路:链表中最为经典的反转链表。在我看到这个题的第一眼想到的方法就是直接模拟反转过程,多申请一个链表来储存已经反转的节点的next即可,在写完这个方法之后,又想到了更加直接的方法,就是利用递归到尾节点,从尾节点开始反转,总的来说,万物皆可递归。

//模拟
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL || head->next == NULL){
            return head;
        }
        ListNode * tail, *newHead = NULL;
        tail = head;
        while(tail != nullptr){
            ListNode *pre = tail->next;
            tail->next = newHead;
            newHead = tail;
            tail = pre;
        }
        return newHead;
    }
};

//递归
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == NULL || head->next == NULL) {
            return head;
        }
        ListNode * res = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return res;
    }
};

剑指Offer22

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

思路:首先我们最直观的思路就是直接遍历一遍链表获取链表长度,然后再次遍历输出倒数第k个节点即可,这样需要遍历两遍。然后我们可以思考怎么只让它遍历一遍就能得到答案,倒数第k个节点其实就是第n-k个节点,我们只要确保一个节点从头节点走n-k步就好了,我们就可以创建两个指针,让一个指针先走k步,然后两个指针一起走,后面这个指针就只走了n-k步了。

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        if(head == NULL || k == 0){
            return head;
        }
        ListNode *pre = head;
        for(int i = 0; i < k; i++){
            if(pre != NULL){
                pre = pre->next;
            }else{
                return NULL;
            }
        }
        while(pre != NULL){
            pre = pre->next;
            head = head->next; 
        }
        return head;
    }
};

剑指Offer52

输入两个链表,找出它们的第一个公共节点。

思路:分别设两个链表的长度为a和b,设公共部分长度为c。一个指针指向A,一个指针指向B,指针A遍历完A之后指向B的头节点遍历至第一个相交的距离为a+b-c,指针B遍历完B之后指向A的头节点遍历至交点的距离为b+a-c,两者的长度相等,此时只有两种情况:

若A和B有交点,此时的位置为第一个公共节点

若A和B没有交点,此时的位置皆为NULL。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *a = headA, *b = headB;
        while(a != b){
            a = a != NULL? a->next : headB;
            b = b != NULL? b->next : headA;
        }
        return a;
    }
};

剑指Offer06

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)

输入:head = [1,3,2]
输出:[2,3,1]

思路:看到遍历链表的第一个思路就是递归,直接递归到尾节点,一个个的存进去即可。

class Solution {
public:
    vector<int> res;
    vector<int> reversePrint(ListNode* head) {
        dp(head);
        return res;
    }
private:
    void dp(ListNode* head){
        if(head == NULL){
            return;
        }
        dp(head->next);
        res.push_back(head->val);
    }
};

剑指Offer18

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

思路:这个问题较为简单,由于链表不能走回头路,我们就需要通过node->next->val来判断是不是需要删除的节点即可。

class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        if(head->val == val){
                return head->next;
            }
        ListNode * tail = head;
        while(tail->next !=nullptr){
            if(tail->next->val == val){
                tail->next = tail->next->next;
                return head;
            }
            tail = tail->next;
        }
        tail->next = nullptr;
        return head;
    }
};

后记

上周写了个需求,看了下深入理解计算机系统,感觉cs的世界真的宏大,还得加油学习!!!

无标签
评论区
头像
文章目录