单链表专题

来源:互联网 发布:淘宝申请退货在哪里 编辑:程序博客网 时间:2024/05/29 13:37

声明:部分试题、解决方案来自LeetCode,本文旨在学习

1.数据结构

普通的单链表及构造函数

struct Node{    int val;    Node* next;    Node(int value,Node* n = NULL):val(value),next(n){};};

2.合并两个有序序列

合并两个有序序列是合并K个有序序列的子问题

Node* merge_two_sorted_lists(Node* node_1,Node* node_2){    if(node_1 == NULL)        return node_2;    if(node_2 == NULL)        return node_1;    Node* pHead = NULL;    if (node_1->val <= node_2->val)    {        pHead = new Node(node_1->val);        if(node_1 != NULL)            node_1 = node_1->next;    }     else    {        pHead = new Node(node_2->val);        if(node_2 != NULL)            node_2 = node_2->next;    }    pHead->next = merge_two_sorted_lists(node_1,node_2);    return pHead;}

3.合并K个有序序列

对于这个问题,一般会暴力地想到每次从所有单链表中选择一个最小的加到最终的链表中,但是这样复杂度太高,未充分利用前一次比较的信息,进而我们可以想到优先队列的思路。

这里要分享的是LeetCode上的代码,思路清晰,代码简洁。

Node* merge_K_sorted_lists(vector<Node*> &lists){    if(lists.empty())        return NULL;    while(lists.size() > 1)    {        //cout << (*(lists.begin()))->val << endl;        lists.push_back(merge_two_sorted_lists(lists[0], lists[1]));        lists.erase(lists.begin());        lists.erase(lists.begin());    //vector erase 删除迭代器指定位置元素,返回表示指向下一个值的迭代器    }    return lists.front();}

4.Add Two Numbers

You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)

Output: 7 -> 0 -> 8

Solution :

时间复杂度O(m+n)

ListNode* addTwoNumber(ListNode* la, ListNode* lb){    ListNode dummy(-1); //头结点    int carry = 0;    ListNode* prev = &dummy;    while (la || lb)    {        int va = la == NULL ? 0 : la->val;        int vb = lb == NULL ? 0 : lb->val;        int value = (va+vb+carry)%10;        prev->next = new ListNode(value);//尾插法        carry = (va+vb+carry)/10;        prev = prev->next;        la = la == NULL ? NULL : la->next;        lb = lb == NULL ? NULL : lb->next;    }    if(carry > 0)        prev->next = new ListNode(carry);    return dummy.next;}

5.翻转链表

ListNode* reverse_list(ListNode* node){    ListNode* pPre = NULL;    ListNode* pNode = node;    ListNode* pReversedHead = NULL;    while (pNode)    {        ListNode* pNext = pNode->next;        if (pNext == NULL)            pReversedHead = pNode;        pNode->next = pPre;        pPre = pNode;        pNode = pNext;    }    return pReversedHead;}

6.Reverse Nodes in k-Group

Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.

If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.

You may not alter the values in the nodes, only nodes itself may be changed.

Only constant memory is allowed.

For example, Given this linked list: 1->2->3->4->5
For k = 2, you should return: 2->1->4->3->5
For k = 3, you should return: 3->2->1->4->5

Solution :

递归版,时间复杂度O(n),空间复杂度O(1)

ListNode* reverseKGroup(ListNode* head, int k){    if(head == NULL || head->next == NULL || k < 2)        return head;    ListNode* next_group = head;    for (int i = 0; i < k; i++)    {        if(next_group)            next_group = next_group->next;        else            return head;    }    //next_group is the head of next group    //new_next_group is the new head of next group after reversion    ListNode* new_next_group = reverseKGroup(next_group,k);    ListNode* prev = NULL;    ListNode* cur = head;    while (cur != next_group)    {        ListNode* next = cur->next;        cur->next = prev ? prev : new_next_group;        prev = cur;        cur = next;    }    return prev; //prev will be the new head of this group  }

7.Remove Duplicates from Sorted List

Given a sorted linked list, delete all duplicates such that each element appear only once.

For example,
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.

Solution:

迭代版,时间复杂度O(n),空间复杂度O(1)

ListNode* drop_duplicates_node(ListNode* node){    if (node == NULL || node->next == NULL)        return node;    ListNode dummy(-1,node);    ListNode* pNext = node->next;    while (node && pNext)    {        if (node->val == pNext->val)        {            ListNode* tmp = pNext;            node->next = pNext->next;            pNext = pNext->next;            delete tmp;        }         else        {            node = pNext;            pNext = pNext->next;        }    }    return dummy.next;}

8.Remove Duplicates from Sorted List II

Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

For example,
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.

Solution :

递归版,时间复杂度O(n),空间复杂度O(1)

ListNode* deleteDuplicates(ListNode* head){    if(head == NULL || head->next == NULL)        return head;    ListNode* p = head->next;    if (head->val == p->val)    {        while (p && head->val == p->val)        {            ListNode* tmp = p;            p = p->next;            delete tmp;        }        delete head;        return deleteDuplicates(p);        //将上面两行替换为注释的这两行,即可解决drop duplicates I        //node->next = drop_duplicates(p);        //return node;    }     else    {        head->next = deleteDuplicates(head->next);        return head;    }}

9.Remove Nth Node From End of List

Given a linked list, remove the nth node from the end of list and return its head.

For example, Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.

Note:
• Given n will always be valid.
• Try to do this in one pass.

Solution:

设两个指针p; q,让q 先走n 步,然后p 和q 一起走,直到q 走到尾节点,删除p->next 即可。

也可以直接通过计算length-k得到直接需要走多少步

ListNode* removeNthFromEnd(ListNode* head, int k){    ListNode dummy(-1,head); //定义头结点    ListNode* p = &dummy;    ListNode* q = &dummy;    for (int i = 0; i < k; i++) //q先走n步        q = q->next;    while (q->next)    {        p = p->next;        q = q->next;    }    ListNode* tmp = p->next;    p->next = p->next->next;    delete tmp;    return dummy.next;}

10.Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Follow up: Can you solve it without using extra space?

Example:

1 -> 2 -> 3 -> 4 -> 5 -> 3 。。。
输出节点3

分析:

Solution:

时间复杂度O(n),空间复杂度O(1)

ListNode* beginOfCycle(ListNode* head){    ListNode* slow = head;    ListNode* fast = head;    while (fast && fast->next)    {        slow = slow->next;        fast = fast->next->next;        if (slow == fast)        {            ListNode* slow2 = head;            while (slow2 != slow)            {                slow2 = slow2->next;                slow = slow->next;            }            return slow2;        }    }    return nullptr;}

11.Partition List

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.

You should preserve the original relative order of the nodes in each of the two partitions.

For example,
Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.

Solution:

时间复杂度O(n),空间复杂度O(1)

ListNode* partition(ListNode* node, int x){    ListNode left_dummy(-1); //头结点    ListNode right_dummy(-1);     ListNode* left_cur = &left_dummy;    ListNode* right_cur = &right_dummy;    while(node)    {        if (node->val < x)        {            left_cur->next = node;            left_cur = node;        }        else        {            right_cur->next = node;            right_cur = node;        }        node = node->next;    }    //链接    left_cur->next = right_dummy.next;    right_cur->next = NULL;    return left_dummy.next;}

12.两个链表中的第一个公共节点

面试这道题的时候很多的面试者第一反应就是采用蛮力的方法:在第一个链表上顺序遍历每个节点,每遍历到一个节点的时候,在第二个链表上顺序遍历每个节点。如果第二个链表上的节点和第一个链表上的节点一样,就说明两个链表在节点上重合,于是就找到了公共的节点。而通常蛮力并不是好的方法。

从链表的定义可以看出,这两个链表是单链表,如果两个链表有公共节点,那么这两个链表从某一节点开始,它们的m_pNext都指向同一个节点,之后它们所有的节点都是重合的,不可能再出现分叉。所以拓扑形状看起来是Y型。

一个简单的方法是:首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个节点。在第二次遍历的时候,先在较长的节点上走若干步,接着同时在两个链表上遍历,找到的第一个相同的节点就是它们的公共的节点。

具体实现的程序如下:

ListNode* FindFirstCommonNode(ListNode* pHead1,ListNode *pHead2){    //得到两个链表的长度    unsigned int nLength1 = GetListLength(pHead1);    unsigned int nLength2 = GetListLength(pHead2);    int nLengthDif = nLength1 - nLength2;    ListNode *pHeadLong = pHead1;    ListNode *pHeadShort = pHead2;    if(nLength2 > nLength1 )    {        ListNode *pHeadLong = pHead2;        ListNode *pHeadShort = pHead1;     nLengthDif = nLength2 - nLength1;    }    //先在长链表上走几步,再同时在两个链表上遍历。    for(int i = 0;i < nLengthDif;i++)        pHeadLong = pHeadLong->m_pNext;    while((pHeadLong != NULL)&&(pHeadShort != NULL)        &&(pHeadLong != pHeadShort ))    {        pHeadLong = pHeadLong->m_pNext;        pHeadShort = pHeadShort->m_pNext;    }    //得到第一个公共节点    ListNode *pFirstCommonNode =    pHeadLong ;        return pFirstCommonNode;}//求链表长度的函数unsigned int GetListLength(ListNode *pHead){    unsigned int Length = 0;    ListNode *pNode = pHead;    while(pNode != NULL)    {        ++Length;        pNode = pNode->m_pNext;    }    return Length;}
0 0
原创粉丝点击