第2周 数据结构-链表

Leetcode 题解 - 链表

链表部分经验谈
  1. 取next前一定先判断当前节点是否为NULL
  2. 更改next前先存个next的备份,否则可能导致无法访问后续节点
  3. 对链表进行分割时不但要关心头节点是否对,还要关心尾是否断开连接
  4. 关于链表的环或公共节点等问题可以往双指针+追及问题考虑
交换链表中的相邻结点

经验:适当定义局部变量减少代码中的->next可以增加代码可读性

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        /*
        思路:每两个一组,可迭代替换,也可递归
        时间复杂度:O(n)
        空间复杂度:O(1)
        */
        // 递归式
        // if(head == NULL || head->next == NULL)
        //     return head;
        // ListNode* tailSwaped = swapPairs(head->next->next);
        // ListNode* nextCopy = head->next;
        // head->next->next = head;
        // head->next = tailSwaped;
        // return nextCopy;

        // 迭代式
        if(head == NULL || head->next == NULL)
            return head;
        ListNode dummyHead = ListNode(0);
        ListNode* headCopy = &dummyHead, *tempHead = &dummyHead;
        tempHead->next = head;
        while(tempHead->next != NULL && tempHead->next->next != NULL){
            // headTemp后面有两个节点才发生交换,交换node1和node2
            ListNode* tailList = tempHead->next->next->next, *node1 = tempHead->next, *node2 = tempHead->next->next;
            node2->next = node1;
            node1->next = tailList;
            tempHead->next = node2;
            tempHead = node1;
        }
        return headCopy->next;
    }
};
链表求和
class Solution {
public:
    int getLen(ListNode* node){
        int len = 0;
        while(node != NULL){
            len += 1;
            node = node->next;
        }
        return len;
    }

    int sumHelper(ListNode* num1, ListNode* num2, int diff){
        // 如果diff<=0,则说明此时的链表num1,num2已经等长
        // 否则num1还比num2更长,还不能直接加
        if(num1 == NULL && num2 == NULL)
            return 0;

        int v1 = num1->val;
        int v2 = diff > 0 ? 0 : num2->val;
        int carry;

        if(diff > 0)
            carry = sumHelper(num1->next, num2, diff-1);
        else
            carry = sumHelper(num1->next, num2->next, diff-1);

        int sum = v1 + v2 + carry;
        num1->val = sum % 10;
        return sum / 10;
    }

    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        /*
        思路:不用栈中转,直接对链表进行相加
        时间复杂度:O(n)
        空间复杂度:O(1)
        */

        // 使得num1的长度不小于num2
        int len1 = getLen(l1), len2 = getLen(l2);
        if(len2 > len1)
            swap(l1, l2);
        int diff = abs(len1 - len2);

        // 链表前增加一个0
        ListNode* num1 = new ListNode(0);
        num1->next = l1;
        diff += 1;

        // 第一个值是0,直接把进位加上就行
        num1->val += sumHelper(num1->next, l2, diff-1);

        return num1->val == 0 ? num1->next : num1;

        // /*
        // 思路:用栈做中转,先将两个链表转入两个栈中,由于栈顶存的低位数所以可以直接对两个栈做加法,将相加后的结果存入栈中,最后再将栈转为链表。
        // 时间复杂度:O(n)
        // 空间复杂度:O(n)
        // */
        // stack<int> sk1, sk2, sum;
        // while(l1 != NULL){
        //     sk1.push(l1->val);
        //     l1 = l1->next;
        // }
        // while(l2 != NULL){
        //     sk2.push(l2->val);
        //     l2 = l2->next;
        // }
        // int carry = 0, tempSum;  // 存进位
        // while(!sk1.empty() || !sk2.empty()){
        //     if(!sk1.empty() && !sk2.empty()){
        //      tempSum = sk1.top() + sk2.top() + carry;
        //      sk1.pop();
        //      sk2.pop();
        //     }
        //     else if(sk1.empty()){
        //      tempSum = sk2.top() + carry;
        //      sk2.pop();
        //     }
        //     else if(sk2.empty()){
        //      tempSum = sk1.top() + carry;
        //      sk1.pop();
        //     }
        //     carry = tempSum / 10;
        //     sum.push(tempSum % 10);
        // }
        // if(carry > 0)
        //     sum.push(carry);
        // ListNode* head = new ListNode(sum.top());
        // ListNode* ans = head;
        // sum.pop();
        // while(!sum.empty()){
        //     head->next = new ListNode(sum.top());
        //     head = head->next;
        //     sum.pop();
        // }
        // return ans;
    }
};
回文链表

思路一:用栈,先得到链表的倒序,然后逐个对比
思路二:在遍历链表的同时,进行反转,如果发现当前节点可能是回文字符串的中心就进行对比
思路三:先用快慢指针找链表中心节点,然后再翻转前半部分,然后判断是否为回文字符串(未实现)

class Solution {
public:
    bool compare(ListNode* a, ListNode* b){
        if(a == NULL && b == NULL)
            return true;
        if(a == NULL || b == NULL)
            return false;
        return a->val == b->val && compare(a->next, b->next);
    }

    bool isPalindrome(ListNode* head) {
        /*
        思路:遍历链表的过程中反转链表,并且寻找链表的中间节点
        时间复杂度:O(n)
        空间复杂度:O(1)
        */
        if(head == NULL || head->next == NULL)
            return true;

        ListNode* revHead = NULL;
        ListNode* seqHead = head;

        while(seqHead != NULL){
            ListNode* seqNext = seqHead->next;
            seqHead->next = revHead;
            revHead = seqHead;
            seqHead = seqNext;
            if(compare(seqHead, revHead))
                return true;
            if(seqHead != NULL && compare(seqHead->next, revHead))
                return true;
        }
        return false;


    }
    bool isPalindrome_stack(ListNode* head) {
        /*
        思路:将链表的值逐个存入栈中,栈弹出的顺序即为反序,然后将其和原始链表对比
        时间复杂度:O(n)
        空间复杂度:O(n)
        */
        stack<int> sk;
        ListNode* headCopy = head;
        while(headCopy != NULL){
            sk.push(headCopy->val);
            headCopy = headCopy->next;
        }
        while(head != NULL){
            if(head->val != sk.top())
                return false;
            head = head->next;
            sk.pop();
        }
        return true;
    }
};
分隔链表

注意:不单单是将子链表的头节点存入数组,还要把后面的部分断掉

class Solution {
public:
    vector<ListNode*> splitListToParts(ListNode* root, int k) {
        /*
        思路:先根据链表长度和k算出指针数组中子链表的长度
        时间复杂度:O(n)
        空间复杂度:O(n)
        */
        if(root == NULL){
            vector<ListNode*> ans;
            for(int i = 0;i < k; i++)
                ans.push_back(NULL);
            return ans;
        }

        int len = 0;
        ListNode* head = root;
        while(head != NULL){
            len += 1;
            head = head->next;
        }
        vector<int> lens(k, len/k);
        int remain = len % k;
        for(int i = 0;i < remain; i++)
            lens[i] += 1;

        vector<ListNode*> ans;
        for(int i = 0;i < k; i++){
            ans.push_back(root);
            int temp_len = 0;
            ListNode* lastElem;
            while(temp_len < lens[i]){
                temp_len += 1;
                lastElem = root;
                root = root->next;
            }
            lastElem->next = NULL;
        }
        return ans;
    }
};
链表元素按奇偶聚集
class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        /*
        思路:创建两个新的head,分别连接奇节点和偶节点
        时间复杂度:O(n)
        空间复杂度:O(1)
        */
        if(head == NULL || (head != NULL && head->next == NULL))
            return head;
        ListNode* oddHead = head;
        ListNode* evenHead = head->next;
        ListNode* oddHeadCopy = oddHead, *evenHeadCopy = evenHead;
        head = head->next->next;
        int cnt = 3;
        while(head != NULL){
            if(cnt % 2 == 1){
                oddHead->next = head;
                oddHead = oddHead->next;
            }
            else{
                evenHead->next = head;
                evenHead = evenHead->next;
            }
            head = head->next;
            cnt += 1;
        }
        oddHead->next = evenHeadCopy;
        evenHead->next = NULL;
        return oddHeadCopy;
    }
};
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值