玩转算法面试:(五)LeetCode链表类问题

206. 反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL)
            return head;
        ListNode *Dummy = new ListNode(0);
        Dummy->next = head;
        while(head->next != NULL)
        {
            ListNode *node = head->next;
            head->next = node->next;
            
            node->next = Dummy->next;
            Dummy->next = node;
        }
        return Dummy->next;
    }
};
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre=NULL;
        ListNode* cur=head;
        while(cur != NULL)
        {
            ListNode* next=cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        
        return pre;
    }
};

92. 反转链表 II

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

采用头插法将后面的元素插到前面来,时间复杂度O(n)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
       ListNode *dummy = new ListNode(0), *cur;
       dummy -> next = head;
       ListNode * pre = dummy;
       for (int i = 0; i < m - 1; i++) {
           pre = pre -> next;
       }
       cur = pre -> next;
       for (int i = 0; i < n - m; i++) {
           ListNode* temp = cur -> next;
           cur -> next = temp -> next;

           temp -> next = pre -> next;
           pre -> next = temp;
       }
       return dummy -> next;
   }
};

86. 分隔链表

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

示例:

输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode* dummy1 = new ListNode(0);
        ListNode* low = dummy1;
        
        ListNode* dummy2 = new ListNode(0);
        ListNode* high = dummy2;
        
        while(head != NULL)
        {
            ListNode* temp = new ListNode(head->val);
            if(temp->val < x)
            {
                low->next = temp;
                low = temp;
            }
            else
            {
                high->next = temp;
                high = temp;
            }
            head = head->next;
        }
        
        low->next = dummy2->next;
        
        return dummy1->next;
    }
};

2. 两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *dummy = new ListNode(-1), *cur = dummy;
        int carry = 0;
        while (l1 || l2) {
            int val1 = l1 ? l1->val : 0;
            int val2 = l2 ? l2->val : 0;
            int sum = val1 + val2 + carry;
            carry = sum / 10;
            cur->next = new ListNode(sum % 10);
            cur = cur->next;
            if (l1) l1 = l1->next;
            if (l2) l2 = l2->next;
        }
        if (carry) cur->next = new ListNode(1);
        return dummy->next;
    }
};

445. 两数相加 II

给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。

 

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

进阶:

如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。

示例:

输入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出: 7 -> 8 -> 0 -> 7

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverse(ListNode* L)
    {
        ListNode* dummy = new ListNode(0);
        dummy->next = L;
        
        ListNode* temp = L;
        while(temp != NULL && temp->next!=NULL)
        {
            ListNode* node = temp->next;
            temp->next = node->next;
            
            node->next = dummy->next;
            dummy->next = node;
        }
        
        return dummy->next;
    }
    
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* L1 = reverse(l1);
        ListNode* L2 = reverse(l2);
        
        ListNode* dummy = new ListNode(0);
        int carry = 0, sum = 0;
        while(L1 || L2)
        {
            int val1 = L1 ? L1->val : 0;
            int val2 = L2 ? L2->val : 0;
            sum = carry + val1 + val2;
            carry = sum / 10;
            ListNode* temp = new ListNode(sum%10);
            temp->next = dummy->next;
            dummy->next = temp;
            
            if(L1) L1 = L1->next;
            if(L2) L2 = L2->next;
        }
        if(carry > 0)
        {
            ListNode* temp = new ListNode(1);
            temp->next = dummy->next;
            dummy->next = temp;
        }
        
        return dummy->next;
    }
};

82. 删除排序链表中的重复元素 II

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        
        ListNode* cur = dummy;
        while(cur->next != NULL)
        {
            ListNode* temp = cur->next;
            if(temp->next == NULL || temp->val != temp->next->val)
                cur = temp;
            else
            {
                while(temp->next != NULL && temp->val == temp->next->val)
                    temp = temp->next;
                cur->next = temp->next;
            }  
        }
        return dummy->next;
    }
};

24. 两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        
        ListNode* cur = dummy;
        ListNode* first = cur->next;
        while(first != NULL)
        {
            ListNode* second = first->next;
            if(second == NULL)
                break;
            first->next = second->next;
            second->next = first;
            cur->next = second;
            
            cur = first;
            first = first->next;
        }
        
        return dummy->next;
    }
};

25. K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

示例 :

给定这个链表:1->2->3->4->5

当 k = 2 时,应当返回: 2->1->4->3->5

当 k = 3 时,应当返回: 3->2->1->4->5

说明 :

    你的算法只能使用常数的额外空间。
    你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* dummy = new ListNode(0);
        ListNode* cur = dummy;
        
        stack<ListNode*> s;
        while(head)
        {
            s.push(head);
            head = head->next;
            
            if(s.size() == k)
            {
                while(!s.empty())
                {
                    ListNode* temp = s.top();
                    s.pop();
                    cur->next = temp;
                    cur = temp;
                } 
            }
        }
        cur->next = NULL;
        while(!s.empty())
        {
            ListNode* temp = s.top();
            s.pop();
            temp->next = cur->next;
            cur->next = temp;
        } 
        
        return dummy->next;
    }
};

147. 对链表进行插入排序

class Solution {
public:
    ListNode* insertionSortList(ListNode* head) {
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        
        ListNode* cur = dummy->next;
        while(cur != NULL)
        {
            ListNode* node = cur->next;
            if(node == NULL)
                break;
            if(cur->val <= node->val)
                cur = node;
            else
            {
                cur->next = node->next;
            
                ListNode* temp = dummy;
                while(temp->next->val < node->val )
                    temp = temp->next;
            
                node->next = temp->next;
                temp->next = node;
            }
                
        }
        
        return dummy->next;
    }
};

148. 排序链表

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4

示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5

第一种方法:快速排序

class Solution {
public:
    void swap(ListNode* s, ListNode* t)
    {
        int temp = s->val;
        s->val = t->val;
        t->val = temp;
    }
    ListNode* partition(ListNode* head, ListNode* tail)
    {
        int pivot = head->val;
        ListNode* cur = head;
        ListNode* temp = cur->next;
        while(temp != tail)
        {
            if(temp->val < pivot)
            {
                cur = cur->next;
                swap(temp, cur);
                
            }
            temp = temp->next;
        }
        swap(cur, head);
        return cur;
    }
        
    void quicksort(ListNode* head, ListNode* tail)
    {
        if(head == tail || head->next == NULL)
            return;
        
        ListNode* mid = partition(head, tail);
        quicksort(head, mid);
        quicksort(mid->next, tail);
    }
    
    ListNode* sortList(ListNode* head) {
        quicksort(head, NULL);
        return head;
    }
};

第二种方法:归并排序

归并排序大致思想是: 1.将链表划分左右两部分--> 2.对左右链表进行排序 --> 3.将左右两个有序链表进行合并
class Solution {
public:       
    ListNode* merge(ListNode* L, ListNode* R)
    {
        ListNode* dummy = new ListNode(0);
        ListNode* cur = dummy;
        while(L != NULL && R != NULL)
        {
            if(L->val < R->val)
            {
                cur->next = L;
                cur = cur->next;
                L = L->next;
            }
            else
            {
                cur->next = R;
                cur = cur->next;
                R = R->next;
            }
        }
        if(L != NULL)
            cur->next = L;
        if(R != NULL)
            cur->next = R;
        
        return dummy->next;
    }
    
    ListNode* mergesort(ListNode* head)
    {
        if(head->next == NULL)
            return head;
        ListNode* first = head;
        ListNode* second = head;
        ListNode* pre = NULL;
        while(second != NULL && second->next != NULL)
        {
            pre = first;
            first = first->next;
            second = second->next->next;
        }
        pre->next = NULL;
  
        ListNode* L = mergesort(head);
        ListNode* R = mergesort(first);
        return merge(L, R);
    }
    
    ListNode* sortList(ListNode* head) {
        if(head == NULL || head->next == NULL)
            return head;
        return mergesort(head);
    }
};

61. 旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL

示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(head == NULL || k == 0)
            return head;
        ListNode *cur = head;
        int count = 1;
        while(cur->next)
        {
            count++;
            cur = cur->next;
        }
        cur->next = head;//变成环
        
        k = k % count;
        int n = count-k;
        for(int i = 0; i < n;i++)
            cur = cur->next;
        ListNode *newNode = cur->next;
        cur->next = NULL;//断开环
        return newNode;
    }
};

143. 重排链表

给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

给定链表 1->2->3->4, 重新排列为 1->4->2->3.

示例 2:

给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.

(1)首先链表使用快慢指针,找到链表的中点

(2)然后将链表的后半部分逆置

(3)最后将后半部分依次插入前半部分

class Solution {
public:   
    void reorderList(ListNode* head) {
        if(head == NULL || head->next == NULL)
            return;
        ListNode* first = head;
        ListNode* second = head;
        while(second != NULL && second->next != NULL)
        {
            first = first->next;
            second = second->next->next;
        }
        
        ListNode* back = first->next;
        while(back != NULL)
        {
            ListNode* temp = back->next;
            if(temp == NULL)
                break;
            back->next = temp->next;
            
            temp->next = first->next;
            first->next = temp;
        }
        
        ListNode* s = head;
        ListNode* t = first;
        while(t->next != NULL)
        {
            ListNode* node = t->next;
            t->next = node->next;
            
            node->next = s->next;
            s->next = node;
            s = node->next;
        }
    }
};

234. 回文链表

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

(1)使用快慢指针找到链表的中点

(2)将后半部分链表逆置

(3)比较前半部分和后半部分

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head == NULL)
            return true;
        ListNode *fast = head, *slow = head;
        while(fast->next != NULL && fast->next->next != NULL)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        
        ListNode* cur = slow->next;
        while(cur != NULL)
        {
            ListNode* temp = cur->next;
            if(temp == NULL)
                break;
            
            cur->next = temp->next;
            temp->next = slow->next;
            slow->next = temp;
        }
        slow = slow->next;
        
        while(slow != NULL)
        {
            if(slow->val != head->val)
                return false;
            head = head->next;
            slow = slow->next;
        }
        return true;
    }
};

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值