leetcode-链表

LeetCode-链表

LeetCode 206 - Reverse Linked List - 反转链表 - easy

反转一个单链表。

示例:

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

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

三步:备份、更新、移动。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode * newHead = NULL;
        ListNode *next = NULL;
        while(head)
        {
            next = head->next;
            head->next = newHead;
            newHead = head;
            head = next;
        }
        return newHead;
    }
};

LeetCode 92 - Reverse Linked List II - 反转链表 2 - medium

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

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

示例:

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

如示例所示,需要保存1和5。

其次,要考虑m=1的情况。

/**
 * 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) {
        
        int nReverseLen = n - m + 1;
        // if m != 1, return head
        ListNode *result = head;

        // find (m - 1) and m
        ListNode *preHead = NULL;
        while(head && --m)
        {
            // if m == 1, preHead == NULL
            preHead = head;
            head = head->next;
        }
        
        ListNode *newHead = NULL;
        // find n+1
        ListNode *tail = head;
        while(head && nReverseLen--)
        {
            ListNode *next = head->next;
            head->next = newHead;
            newHead = head;
            head = next;
        }
        // 2->5
        tail->next = head;
        
        if (preHead)
        {
            preHead->next = newHead;
        }
        else
        {
            // m == 1
            result = newHead;
        }
        return result;
    }
};

LeetCode 160 - Intersection of Two Linked Lists - 相交链表 - easy

编写一个程序,找到两个单链表相交的起始节点。

  • 如果两个链表没有交点,返回 null.
  • 在返回结果后,两个链表仍须保持原有的结构。
  • 可假定整个链表结构中没有循环。
  • 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

解法1

可以借助set容器保存一个链表节点的地址,再和另一链表进行节点地址匹配。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        std::set<ListNode*> setNode;
        while(headA)
        {
            setNode.insert(headA);
            headA = headA->next;
        }
        while(headB)
        {
            if ( setNode.find(headB) != setNode.end() )
            {
                return headB;
            }
            headB = headB->next;
        }
        return NULL;
    }
};

方法2,空间复杂度O(1)

获取两个链表长度,用两个指针指向距离末尾节点长度为n的节点,n为短链表长度,然后遍历。

class Solution {
public:
    int getListLen(ListNode *head)
    {
        int nLen = 0;
        while(head)
        {
            head = head->next;
            ++nLen;
        }
        return nLen;
    }
    ListNode* forwardLongList(int nDelta, ListNode *head)
    {
        while(head && nDelta)
        {
            head = head->next;
            --nDelta;
        }
        return head;
    }
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) 
    {
        int nLenA = getListLen(headA);
        int nLenB = getListLen(headB);

        if(nLenA > nLenB)
        {
            headA = forwardLongList(nLenA - nLenB, headA);
        }
        else if(nLenA < nLenB)
        {
            headB = forwardLongList(nLenB - nLenA, headB);
        }

        while(headA && headB)
        {
            if (headA == headB)
            {
                return headA;
            }
            headA = headA->next;
            headB = headB->next;
        }
        return NULL;
    }
};

LeetCode 141/142 - Linked List Cycle - easy

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

解法1

使用set容器。

class Solution {
public:
    bool hasCycle(ListNode *head) {
        std::set<ListNode*> setNode;
        while(head)
        {
            if(setNode.find(head->next) != setNode.end())
            {
                return true;
            }
            setNode.insert(head);
            head = head->next;
        }
        return false;
    }
};

解法2 快慢指针

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head;
        ListNode *slow = head;
        ListNode *meet = NULL;
        while(fast)
        {
            //先各走一步
            fast = fast->next;
            slow = slow->next;
            if (!fast)
            {
                return NULL;
            }
            fast = fast->next;
            if(fast == slow) 
            {
                meet = fast;
                break;
            }
        }
        if (!meet) return NULL;
        while(head)
        {
            if(head == meet)
            {
                return head;
            }
            head = head->next;
            meet = meet->next;
        }
        return NULL;
    }
};

LeetCode 86 - Partition List - 分隔链表 - medium

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

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

示例:

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

需要借助局部临时节点。

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode bigList(0);
        ListNode smallList(0);
        ListNode *pBigList = &bigList;
        ListNode *pSmallList = &smallList;

        while(head)
        {
            if(head->val < x)
            {
                pSmallList->next = head;
                pSmallList = head;
            }
            else
            {
                pBigList->next = head;
                pBigList = head;
            }
            head = head->next;
        }
        pBigList->next = NULL;
        pSmallList->next = bigList.next;
        return smallList.next;
    }
};

LeetCode 138 - Copy List with Random Pointer - 复制带随机指针的链表 - medium

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的 深拷贝。

我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。

难点在于,拷贝随机指针。

class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};

可以多耗费点空间,用map存储[node, ord],即节点地址和序号键值对,然后用vector保存新链表各个节点地址(可以看成[ord, node])。

class Solution {
public:
    Node* copyRandomList(Node* head) {
        std::map<Node*, int> mapNode;
        std::vector<Node*> vecNode;
        Node *pNode = head;
        int i = 0;
        while(pNode)
        {
            vecNode.push_back(new Node(pNode->val));
            // map address to ord
            mapNode[pNode] = i++;
            pNode = pNode->next;
        }
        vecNode.push_back(NULL);

        // 连接next, random
        pNode = head;
        i = 0;
        while(pNode)
        {
            vecNode[i]->next = vecNode[i+1];

            if(pNode->random)
            {
                int nTmp = mapNode[pNode->random];
                vecNode[i]->random = vecNode[nTmp];
            }

            pNode = pNode->next;
            ++i;
        }
        return vecNode[0];
    }
};

LeetCode 21 - Merge Two Sorted Lists - easy

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

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

和86题一样,像这种返回新链表的函数,都需要借助临时头节点。

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode preHead(0);
        ListNode *pNode = &preHead;
        while(l1 && l2)
        {
            if( l1->val < l2->val )
            {
                pNode->next = l1;
                l1 = l1->next;
            }
            else
            {
                pNode->next = l2;
                l2 = l2->next;
            }
            pNode = pNode->next;
        }
        if (l1)
        {
            pNode->next = l1;
        }
        if (l2)
        {
            pNode->next = l2;
        }
        return preHead.next;
    }
};

LeetCode 23 - Merge k Sorted Lists - hard

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

方法1是两两合并,即合并k-1次,但假设平均每个链表n各节点,(n+n) + (2n+n) + .. + ((k-1n) + n),会超时。

方法2是将kn各节点放在vector中排序,然后链接。排序最优复杂度就是O(kn logkn).

class Solution {
public:
    static bool cmp(const ListNode *a, const ListNode *b)
    {
        return a->val < b->val;
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        std::vector<ListNode*> vecNode;
        for(int nSize = lists.size(), i = 0; i < nSize; ++i)
        {
            ListNode *pNode = lists[i];
            while(pNode)
            {
                vecNode.push_back(pNode);
                pNode = pNode->next;
            }
        }
        if(vecNode.empty()) return NULL;

        std::sort(vecNode.begin(), vecNode.end(), cmp);
        for(int nSize = vecNode.size(), i = 1; i < nSize; ++i)
        {
            vecNode[i - 1]->next = vecNode[i];
        }
        vecNode.back()->next = NULL;
        return vecNode[0];
    }
};

最优方法是对k个链表分治,两两合并。

  1. 第一轮,处理k/2个链表,每次2n个节点;
  2. 第二轮,处理k/4个链表,每次4n个节点;
  3. 。。。
  4. 最后一轮,处理k/(2^logk)次,每次n*2^logk个节点。
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode preHead(0);
        ListNode *pNode = &preHead;
        while(l1 && l2)
        {
            if( l1->val < l2->val )
            {
                pNode->next = l1;
                l1 = l1->next;
            }
            else
            {
                pNode->next = l2;
                l2 = l2->next;
            }
            pNode = pNode->next;
        }
        if (l1)
        {
            pNode->next = l1;
        }
        if (l2)
        {
            pNode->next = l2;
        }
        return preHead.next;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int nSize = lists.size();
        if (nSize == 0) return NULL;
        if (nSize == 1) return lists[0];
        if (nSize == 2) return mergeTwoLists(lists[0], lists[1]);
        
        int nMid = nSize / 2;
        std::vector<ListNode*> subList_1, subList_2;
        for(int i = 0; i < nMid; ++i)
        {
            subList_1.push_back(lists[i]);
        } 
        for(int i = nMid; i < nSize; ++i)
        {
            subList_2.push_back(lists[i]);
        }
        ListNode *l1 = mergeKLists(subList_1);
        ListNode *l2 = mergeKLists(subList_2);
        return mergeTwoLists(l1, l2);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值