【算法学习】链表篇:如何实现链表的相关操作

链表基础

指针元素引用 head->val

对象元素引用 head.val

struct ListNode {
    int val;
    ListNode *next;
};

int main(){
    ListNode a;
    ListNode b;
    a.val = 1;
    a.next = &b;
    ListNode *head = &a;
    while(head){
        cout << head->val << endl;
        head = head->next;
    }
}

链表逆序

整体逆序
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *new_head = NULL;
        while(head){
            ListNode* next = head->next;
            head->next = new_head;
            new_head = head;
            head = next;
        }
        return new_head;
    }
};
部分逆序
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        int change_len = n - m + 1; //需要逆序的个数
        ListNode *pre_head = NULL;
        ListNode *result = head;
        while(head && --m){ //找到逆序开始点位置head以及前一个位置pre_head
            pre_head = head;
            head = head->next;
        }
        ListNode *modify_list_tail = head; //记录逆序后尾节点位置
        ListNode *new_head = NULL; //new_head比head落后一个位置
        while(head && change_len){
            ListNode *next = head->next;
            head->next = new_head;
            new_head = head;
            head = next;
            change_len --;
        }
        modify_list_tail->next = head;
        if (pre_head){
            pre_head->next = new_head;
        }
        else{
            result = new_head;
        }
        return result;
    }
};

链表求交点

思路1:使用set求交点

遍历链表A,将A中节点指针地址插入set

遍历B,查看B中节点指针地址是否出现在set中

class Solution {
public:
    ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
        set<ListNode*> node_set;
        while(headA){
            node_set.insert(headA);
            headA = headA->next;
        }
        while(headB){
            if(node_set.find(headB) != node_set.end())
                return headB;
            headB = headB->next;
        }
        return NULL;
    }
};
思路2:获取A、B长度,由差加双指针得知交点位置

遍历链表A、B,获取长度

遍历B n-m次

双指针同时移动,对比指针所指节点地址

class Solution {
public:
    ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
        int list_A_len= get_list_length(headA);
        int list_B_len= get_list_length(headB);
        if(list_A_len > list_B_len){
            headA = forward_long_list(list_A_len, list_B_len, headA);
        }
        else{
            headB = forward_long_list(list_B_len, list_A_len, headB);
        }
        while(headA && headB){
            if(headA == headB)
                return headA;
            headA = headA->next;
            headB = headB->next;
        }
        return NULL;
    }
    int get_list_length(ListNode* head){
        int len = 0;
        while(head){
            len ++;
            head = head->next;
        }
        return len;
    }
    ListNode* forward_long_list(int long_len, int short_len, ListNode* head){
        int delta = long_len - short_len;
        while(head && delta){
            head = head->next;
            delta --;
        }
        return head;
    }
};

链表求环

思路1:使用set求交点

遍历链表A,如果节点地址不存在,则将当前节点地址加入set集合

class Solution {
public:
    ListNode* getIntersectionNode(ListNode* head) {
        set<ListNode*> node_set;
        while(head){
            if(node_set.find(head) != node_set.end())
                return head;
            node_set.insert(head);
            headB = headB->next;
        }
        return NULL;
    }
};
思路2:快慢指针

快指针一次走两步,慢指针一次走一步,当相遇时,当前节点位置与交点距离 = 头节点与交点距离

头---------交点----------相遇的地方
            |                    |
            |                    |
            |                    |
            ---------------------

记头到交点距离为a,交点到相遇地方为b,相遇地方到交点距离为b或c

慢指针从头走到相遇的地方,行进距离:a+b;快指针行进距离:2(a+b)
则有2(a+b)=(a+b)+ c + b ==>  a=c
class Solution {
public:
    ListNode* getIntersectionNode(ListNode* head) {
        ListNode* fast = head;
        ListNode* slow = head;
        ListNode* meet = NULL;
        while(fast){
            slow = slow->next;
            fast = fast->next;
            if (!fast){
                return NULL;
            }
            fast = fast->next;
            if (fast == slow){
                meet = fast;
                break;
            }
        }
        if (meet == NULL){
            return NULL;
        }
        while(head && meet){
            if (head == meet){
                return head;
            }
            head = head->next;
            meet = meet->next;
        }
        return NULL;
    }
};

链表划分

题目说明

已知链表头指针head与数值x,将所有小于x的节点放在大于或等于x的节点前,且保持原有相对位置

思路1:巧用临时头节点

双指针:一个记录小于x的节点地址;一个记录大于x的节点地址,最后将两者相连

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode less_head(0);
        ListNode more_head(0);
        ListNode* less_ptr = &less_head;
        ListNode* more_ptr = &more_head;
        while(head){
            if(head->val < x){
                less_ptr->next = head;
                less_ptr = head;
            }
            else{
                more_ptr->next = head;
                more_ptr = head;
            }
            head = head->next;
        }
        less_ptr->next = more_head.next;
        more_ptr->next = NULL;
        return less_head.next;
    }
};

复杂链表复制

题目说明

已知一个复杂的链表,节点中有一个指向本链表任意某个节点的随机指针,求链表的深度拷贝

struct RandomListNode {
    int label;
    ListNode *next, *random;
    RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
};
思路1:使用map将节点地址与节点序号对应起来
class Solution {
public:
    RandomListNode* copyRandomList(RandomListNode* head) {
        map<RandomListNode *, int> node_map;
        vector<RandomListNode *> node_vec;
        RandomListNode *ptr = head;
        int i = 0;
        while(ptr){
            node_vec.push_back(new RandomListNode(ptr->label));
            node_map[ptr] = i;
            ptr = ptr->next;
            i ++;
        }
        node_vec.push_back(0);
        ptr = head;
        i = 0;
        while(ptr){
            node_vec[i]->next = node_vec[i+1];
            if (ptr->random){
                int id = node_map[ptr->random];
                node_vec[i]->random = node_vec[id];
            }
            ptr = ptr->next;
            i ++;
        }
        return node_vec[0];
    }
};

2个排序链表归并

题目说明

已知两个已排序链表,将其合并,并保证合并后依然有序

思路1:双指针
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode temp_head(0);
        ListNode *pre = &temp_head;
        while(l1 && l2){
            if (l1->val < l2->val){
                pre->next = l1;
                l1 = l1->next;
            }
            else{
                pre->next = l2;
                l2 = l2->next;
            }
            pre = pre->next;
        }
        if (l1){
            pre->next = l1;
        }
        if (l2){
            pre->next = l2;
        }
        return temp_head.next;
    }
};

K个排序链表归并

思路1:暴力合并 递归合并 k个链表需k-1次
思路2:排序后相连
class Solution {
public:
    bool cmp(const ListNode *a, const ListNode *b){
        return a->val < b->val;
    }
    ListNode* mergeKLists(vector<ListNode* >& lists) {
        vector<ListNode *> node_vec;
        for (int i = 0; i < lists.size(); i++){
            ListNode* head = lists[i];
            while(head){
                node_vec.push_back(head);
                head = head->next;
            }
        }
        if(node_vec.size() == 0)
            return NULL;
        sort(node_vec.begin(), node_vec.end(), cmp);
        for (int i = 1; i < node_vec.size(); i++){
            node_vec[i-1]->next = node_vec[i];
        }
        node_vec[node_vec,size()-1]->next = NULL;
        return node_vec[0];
    }
};
思路3:分治 将k分为若干组 分别排序
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode* >& lists) {
        if (lists.size() == 0)
        return NULL;
        if (lists.size() == 1)
            return lists[0];
        if (lists.size() == 2)
            return mergeTwoLists(lists[0], lists[1]);
        int mid = lists.size() / 2;

        vector<ListNode* >& sub1_lists;
        vector<ListNode* >& sub2_lists;
        for (int i = 0; i < mid; i++){
            sub1_lists.push_back(Lists[i]);
        }
        for (int i = mid; i < lists.size(); i++){
            sub2_lists.push_back(Lists[i]);
        }   
        ListNode* l1 = mergeKLists(sub1_lists);
        ListNode* l2 = mergeKLists(sub2_lists);

        return mergeTwoLists(l1, l2);
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值