c++ 怎样连接两个链表_【数据结构】Leetcode链表经典题

点击蓝字

309833914017383587aa4491e675d053.gif

关注我们

Contents
    • 1 找出两个链表的交点 (160)

    • 2 链表反转 (206)

    • 3 归并两个有序的链表 (21)

    • 4 从有序链表中删除重复节点(83)

    • 5 删除链表的倒数第 n 个节点(19)

    • 6 交换链表中的相邻结点 (24)

    • 7 链表求和(445)

    • 8 回文链表(234)

    • 9 分隔链表(725)

    • 10 链表元素按奇偶聚集(328)


1 找出两个链表的交点 (160)

     题目:编写一个程序,找到两个单链表相交的起始节点。 d41972bc302190eb0cf0263cadb919d3.png      思路1:此题的目的是判断两个链表共同拥有的部分的起始地址。首先将不等长的链表从尾部对齐,这样就保证了最后的共同拥有的部分已经对齐,之后只需将较长的链表先移动与和短的链表平齐,然后一起向后遍历,找到结点地址相等的节点返回即可。可以自己定义一个获取链表长度的函数length()。
class Solution {public:    int length(ListNode *A){        int len = 0;        while(A->next != NULL){            len++;            A = A->next;        }        return len;    }    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {        ListNode *A = headA; ListNode *B = headB;        if(A==NULL||B==NULL) return NULL;        int len_a = length(headA);        int len_b = length(headB);        for(; len_a            B = B->next;        }        for(; len_a>len_b; len_a--){            A = A->next;        }        while(A != NULL && A!=B){            A = A->next;            B = B->next;        }        return A;    }};
     思路2:设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。     当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。     如果不存在交点,那么 a + b = b + a,以下实现代码中 l1 和 l2 会同时为 null,从而退出循环。
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? headA:A->next;           B = B == NULL? headB:B->next;        }        return A;    }};
     两个思路的对比: 06319aff2175179cc8bcb46eb088cf73.png     通过结果可以看出两个思路内存消耗相同的情况下,思路2在运行时间上要大得多。

2 链表反转 (206)

示例:      输入: 1->2->3->4->5->NULL      输出: 5->4->3->2->1->NULL 思路: 69fa85f044a08b81f23850a549afd90e.gif     涉及到链表反转等改变链表的连接结构时, 不可以用 pre=pre->next 来向前推进节点 因为已经修改了链表的原有顺序 !!!需用 ListNode* temp = pre->next;来时刻记录pre-next的节点。
class Solution {public:    ListNode* reverseList(ListNode* head) {        ListNode* cur = NULL;        ListNode* pre = head;        if(head == NULL) return head;        while(pre != NULL){            ListNode* temp = pre->next;            pre->next = cur;            cur = pre;            pre = temp;        }        return cur;    }};

3 归并两个有序的链表 (21)

     将两个升序链表合并为一个新的 升序(非降) 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例:      输入:1->2->4, 1->3->4      输出:1->1->2->3->4->4 思路:     首先任意定义一个头节点result,只是起到一个对之后排序的节点的领头作用,最后返回时仅返回result->next即可。
  1. 定义头结点
  2. 若 l1 指向的结点值 >= l2 指向的结点值,则将 l1 链接到头结点的 next 位置; 否则将 l2 链接到头结点的 next 位置;
  3. 循环进行,直至 l1 或 l2 为 NULL;
  4. 最后,将 l1 或 l2 中剩余部分,链接到头结点后面;
class Solution {public:    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {        ListNode* result = new ListNode(1);        ListNode* end = result;        while(l1 != nullptr && l2 != nullptr){            if(l1->val >= l2->val){                result->next = l2;                l2 = l2->next;            }            else{                result->next = l1;                l1 = l1->next;            }            result = result->next;        }        result->next = l1==nullptr? l2:l1;        return end->next;    } };

4 从有序链表中删除重复节点(83)

     给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。 示例:      输入: 1->1->2->3->3      输出: 1->2->3 思路:定义两个节点指针,通过比较节点大小不断修改第一个指针的后继,直到第一个指针的后继为NULL;要注意后继节点succ->next = NULL的情况。
class Solution {public:    ListNode* deleteDuplicates(ListNode* head) {        if(head == NULL || head->next == NULL) return head;        ListNode* cur = head;        ListNode* succ = head->next;        while(cur->next != NULL){            if(cur->val == succ->val){                if(succ->next ==NULL){                    cur->next = NULL;                }                else{                    cur->next = succ->next; // 将当前节点的后继修改为重复元素节点的后继,相当于删掉了重复节点succ;                    succ = succ->next;                }            }            else{                cur = cur->next;                succ = succ->next;            }        }        return head;    }};


5 删除链表的倒数第 n 个节点(19)

     给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 示例:     给定一个链表: 1->2->3->4->5, 和 n = 2.     当删除了倒数第二个节点后,链表变为 1->2->3->5. 思路:首先获取链表长度,根据n可知道删除节点之前的节点个数,将待删除节点之前的节点和之后的节点连接即可。注:若链表长度和n相同,即删除链表的第一个节点,直接返回head->next即可。
class Solution {public:    int length(ListNode* A){        int len = 0;        while(A != NULL){            len++;            A = A->next;        }        return len;    }    ListNode* removeNthFromEnd(ListNode* head, int n) {        if(head == NULL) return head;        if(head->next == NULL && n ==1) return head->next;        ListNode* result = head;        int len = length(head);        if(len == n){            return result->next;        }        for(int i=1; i            head = head->next;        }        head->next = head->next->next;        head = head->next;        while(head!=NULL){            for(int i=0; i1; i++){                head = head->next;            }        }        return result;    }};

6 交换链表中的相邻结点 (24)

     给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 示例:      输入: 1->2->3->4      输出: 2->1->4->3. 思路:head节点以长度2向前推进,需要借助一个辅助头节点来指向head,终止条件为head == NULL || head->next == NULL,分别对应于链表节点个数为偶数和奇数的情况。 1fc0dc6a092cfc1f4f8d4e5f1ed265a3.png
class Solution {public:    ListNode* swapPairs(ListNode* head)     {        //新建一个空结点,用来指向头节点        ListNode* p = new ListNode(0);        p->next = head;        //新建和p相同一个curr节点,两个相同的节点一个是当前做改变的节点,一个是保持不动用来返回的节点        ListNode* curr = p;        //循环条件为当前节点为NULL或当前的下一个节点为NULL,分别对应偶数和奇数个节点的终止标志        while(head != NULL && head->next != NULL)        {            //为了清晰明了,我们新建两个节点,一节点和二节点            ListNode* firstNode = head;            ListNode* secondNode = head->next;            ///把一和二进行交换,并连接前后            //当前curr节点指向二节点            curr->next = secondNode;            //一节点指向二节点此时的下一节点            firstNode->next = secondNode->next;            //二节点指向一节点,即交换位置成功            secondNode->next = firstNode;            //由于每次循环curr节点都指向每次循环的一节点,所以要再次把curr节点指向一节点            curr = firstNode;            //每次移动都是由head节点来赋值操作,所以head应向右移动两格,即新循环的一节点            head = firstNode->next;        }        //返回p的下一个节点即对应整个操作后的链表        return p->next;    }};

7 链表求和(445)

     给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。不能对列表中的节点进行翻转。 示例:      输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)      输出:7 -> 8 -> 0 -> 7 思路: 57825573d3d5b1fd99149c002476c956.png
class Solution {public:    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {        int count=0, temp;        ListNode* head, *last;        for(head = l1; head!=NULL;head=head->next) count++;        for(head = l2; head!=NULL;head=head->next) count--;        if(count<0) swap(l1, l2);        //last = new ListNode(0);        last = head = new ListNode(0);  // last 和 head指向同节点,且该节点值为0 如果分来new,则两个节点指针指向地址不同,会报错。        head->next = l1;        for(int i=abs(count); i!=0; i--){            if(l1->val!=9) last=l1;            l1 = l1->next;        }        while(l1!=NULL){            temp = l1->val + l2->val;            if(temp>9){                temp -= 10;                last->val += 1;                last = last->next;                while(last != l1){                    if(last!=NULL)                    last->val =0;                    last = last->next;                }            }            else if(temp != 9) last = l1;            l1->val = temp;            l1 = l1->next;            l2 = l2->next;        }    return head->val ==1? head:head->next;    }};


8 回文链表(234)

     请判断一个链表是否为回文链表。 实例:      输入: 1->2->2->1      输出: true 思路1:将链表的值依次存入向量中,然后直接按秩取值进行回文判断; 思路2:将链表后半部分进行反转,链表的中间节点为两边两个子链表的共同节点,然后两个子链表按序遍历比较即可。
class Solution {public:    bool isPalindrome(ListNode* head) {        // 时间复杂度、空间复杂度均为o(n)        vector a;        while(head){            a.push_back(head->val);            head = head->next;        }        for(int i =0; i            if(a[i]!=a[a.size()-i-1]) return false;        }        return true;        /* 链表反转后半部分,再逐一比较        if(head==NULL ||head->next==NULL) return true;        ListNode* slow = head;        ListNode* fast = head;        //找到链表中间节点        while(fast!=NULL && fast->next!=NULL){            slow = slow->next;            fast = fast->next->next;        }        ListNode* curr = slow;        ListNode* nextNode = curr->next;        //反转后半部分链表        while(nextNode!=NULL){            ListNode* temp = nextNode->next;            nextNode->next = curr;            curr = nextNode;            nextNode = temp;        }        slow->next = NULL;        //开始逐一比较        while(head!=NULL && curr!=NULL){            if(head->val != curr->val) return false;            head = head->next;            curr = curr->next;        }        return true;        */    }};


9 分隔链表(725)

     给定一个头结点为 root 的链表, 编写一个函数以将链表分隔为 k 个连续的部分。     每部分的长度应该尽可能的相等: 任意两部分的长度差距不能超过 1,也就是说可能有些部分为 null。     这k个部分应该按照在链表中出现的顺序进行输出,并且排在前面的部分的长度应该大于或等于后面的长度。     返回一个符合上述规则的链表的列表。 示例:      输入:1->2->3->4, k = 5 // 5       输出: [ [1], [2], [3], [4], null ] 思路:首先得到链表长度,若然后求出每个子链表应有的长度,保留余数,将余数分散到结果列表中的前几个链表当中;在每个子链表到达其长度后将子链表最后一个节点指向NULL,使得与原链表断开。
class Solution {public:    vector splitListToParts(ListNode* root, int k) {        if(root==NULL) return vector(k, NULL);    ListNode* curr = root;    int len=0;    while(curr!=NULL){        len++;        curr = curr->next;    }    int avglen = len / k;    int res = len % k;    vector result(k, NULL);    ListNode* now = root;    ListNode* prev = NULL;    for(int i=0; i        result[i] = now;        int eachlen = res>0? avglen+1:avglen;        for(int j=0; j// 内层循环处理result的每一位应该包含原始链表的哪些元素            prev = now;            now = now->next;        }        prev->next = NULL;        res--;        }    return result;    }};

10 链表元素按奇偶聚集(328)

     给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。 示例 :      输入: 2->1->3->5->6->4->7->NULL      输出: 2->3->6->7->1->5->4->NULL思路: a6adc34f243450d871308055f19dbe4a.png
class Solution {public:    ListNode* oddEvenList(ListNode* head) {        if(!head) return head;        ListNode* odd = head;        ListNode * even = head->next;        ListNode * evenstart = even;        while(even&&even->next){            odd->next = even->next;            odd = odd->next;            even->next = odd->next;            even = even->next;        }        odd->next = evenstart;        return head;    }};

写留言
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值