zoukankan      html  css  js  c++  java
  • 链表

    Leetcode #160 相交链表

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

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

    方法1:

    1.利用指针及链表特性数出两个链表各自长度
    2.使较长的链表的头指针先向前移动多出来的长度,使得两链表从当前位置处开始长度相等
    3.移动两个指针,直到两个指针值相等时退出循环
    4.返回该指针

    这个方法很显而易见,就不具体阐述了

    方法2:

    我们可以得出链表长度由两部分组成,一部分是公共部分,一部分是非公共部分。

    要点:如果我知道了A链表和B链表各自非公共部分的长度,那么只要走完这个非公共部分的长度,指针所指即是答案。

    假设有链表A、B。 A链表非公共部分长度为a,B链表非公共部分长度为b,公共部分长度为c,那么我们可以可以很容易得出的一个等式: a + c + b = b + c + a其中左边的 a + c 和右边的 b + c 其实就是两个链表各自的长度了。那么这句等式具体是什么意思呢?
    我们可以这样理解:PA 和 PB刚开始是分别指向链表A、B的指针,不断的令PA、PB往下一个结点移动,当PA走完A链表的长度后(也就是走完a + c的距离后),令PA指向B链表的头部,再走一个b的距离(也就是B链表非公共部分的长度),这不就实现了我们的那个要点嘛!!!!

    上代码:

    
        ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
            ListNode *la = headA, *lb = headB;
            while(la != lb)
            {
                la = (la == NULL)? headB : la->next;
                lb = (lb == NULL)? headA : lb->next;
            }
            return la;
        }
    

    Leetcode #206 翻转链表

    题名:翻转链表
    描述:反转一个单链表。分别写出迭代和递归算法。

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/reverse-linked-list/

    方法1:迭代

    迭代无非就是按顺序从头遍历,在遍历的同时就行翻转操作!
    我们可以设置连个三个指针, pre, cur, next。初始时,pre指向NULL,cur指向头结点(注意这里的头结点也是元素节点,并不是空节点),next指向头结点的下一个结点。
    用这三个指针遍历整个数组,每一次将cur的指针指向pre所指的结点,next用于记录下一个结点的位置。结束条件是当cur为空。代码如下:

    
        ListNode* reverseList(ListNode* head) {
            ListNode *pre = NULL, *cur = head;
            while(cur)
            {
                ListNode *next = cur->next;
                cur->next = pre;
                pre = cur;
                cur = next;
            }
            return pre;
        }
    

    还有一种方法,速度要快得多,当然也消耗了多余的空间,是空间换时间的思想。它就是多申请了一个头结点,然后用头插法进行翻转。代码如下:

    
        ListNode* reverseList(ListNode* head) {
            ListNode *nh = new ListNode(-1);
            while(head)
            {
                ListNode *next = head->next;
                head->next = nh->next;
                nh->next = head;
                head = next;
            }
            return nh->next;
        }
    

    方法2:递归

    递归算法思想还是比较好像的,关键在于细节。
    此处再次回顾一下递归的思想:把大问题化成具有相同性质的小问题,在解决小问题后再自然而然地解决大问题。
    关键是:1.递归出口 2.递归式 3.返回值 4.具体实现

    这个翻转链表的问题就是一个典型的递归问题。将一个较长的链表进行翻转,那我就先去翻转较短的子链表。例如要对1->2->3->4->5进行翻转,我就先去翻转2->3->4->5,要对2->3->4->5进行翻转我就先去翻转3->4->5,知道最后仅仅翻转5,这么个最小的问题,所以递归出口也就在这里了。这样大概的就可以写出一个雏形:

    
        ListNode* reverseList(ListNode* head) {
            //先写递归出口
            if(head == NULL || head->next == NULL)
                return head;
            //再写递归式
            ListNode *next = head->next;//指向下一个较短链表
            ListNode *newhead = reverseList(next);//翻转这个较短的链表
            
            ......
            return newhead;//返回值可以马上就确定下来,就是指向翻转后新链表的头指针
        }
    

    这样雏形就出来了,这时候就应该想如何去翻转某个具体的链表。例如翻转4->5,head是指向4的,next是指向5的,那么要变成5->4,就是next->next = head; head->next = NULL;所以具体代码如下:

    
        ListNode* reverseList(ListNode* head) {
            if(head == NULL || head->next == NULL)
                return head;
            ListNode *next = head->next;
            ListNode *reversed = reverseList(next);
            next->next = head;
            head->next = NULL;
            return reversed;
        }
    

    Leetcode #21 合并链表

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

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/merge-two-sorted-lists/

    方法1:常规遍历

    将两个链表合并成一个链表,是链表基本操作!
    为了思考起来比较方便,我们可以引入一个头结点,然后设置一个用于移动遍历的指针p,指向头结点。对l1,l2进行遍历每次穿起较小的那个节点,代码如下:

    
        ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
            ListNode *newlist = new ListNode(-1);
            ListNode *p = newlist;
            while(l1 && l2)
            {
                if(l1->val <= l2->val)
                {
                    p->next = l1;
                    p = l1;
                    l1 = l1->next;   
                }
                else
                {
                    p->next = l2;
                    p = l2;
                    l2 = l2->next; 
                }
            }
            while(l1)
            {
                p->next = l1;
                p = l1;
                l1 = l1->next;
            }
            while(l2)
            {
                p->next = l2;
                p = l2;
                l2 = l2->next;
            }
            return newlist->next;
        }
    

    方法2:递归

    递归思想回顾:
    1.递归出口; 显而易见,当链表为空时,就到了递归的边界了。
    2.递归式;
    3.返回值; 因为是单链表,只有知道链表头指针才能遍历整个链表,所以返回值是指向新链表的指针。
    4.具体实现; 取整个递归过程中,某一层(只需要一层,而不需要完全搞清楚整个递归栈是如何运作的)递归的实现即是具体实现。

    其中递归式,就是用来把大问题化解为小问题的。
    具体实现要取整个递归过程中的某一层递归,取设计其实现。例如这一题:(取第一层递归)假设我们有两链表L1: 1->3->5->7 L2: 2->4->6->8
    因为要求升序,所以合并1->3->5->7和2->4->6->8,相当先合并3->5->7和2->4->6->8并返回新合并链表的头指针newlist(由递归的正确性,我们可以得知newlist链表就是2->3->4->5->6->7->8,而newlist是指向2的),再使1->(指向)返回的头指针newlist,结果就是1->2->3->4->5->6->7->8

    具体代码如下:

    
        ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
            if(l1 == NULL)
                return l2;//返回值永远是指向新链表的指针!
            if(l2 == NULL)
                return l1;
            if(l1->val < l2->val)
            {
                l1->next = mergeTwoLists(l1->next, l2);
                return l1;
            }
            else
            {
                l2->next = mergeTwoLists(l2->next, l1);
                return l2;
            }
        }
    

    Leetcode #83 删除排序链表中重复的元素

    题名:deleteDuplicates
    描述:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/

    方法一:

    因为链表是sorted已排序的,所以可以用双指针法,然后遍历整个链表,较为简单不再叙述,上代码:

    
        ListNode* deleteDuplicates(ListNode* head) {
            ListNode *p = head, *q;
            if(p)   q = p->next;
            bool ndd = false;
            while(q != NULL)
            {
                if(p->val == q->val)
                    ndd = true;
                else
                {
                    if(ndd)
                    {
                        p->next = q;
                        ndd = false;
                    }
                    p = q;
                }
                q = q->next;
            }
            if(ndd)
                p->next = q;
            return head;
        }
    

    方法二:

    可以用递归的方法,能减少代码量:
    这里递归式就是要加一个判断。假设有p,q指针,当p->val != q->val时,再用递归,代码如下:

    
        ListNode* deleteDuplicates(ListNode* head) {
            if(!head)
                return head;
            ListNode *p = head, *q = head->next;
            while(q && p->val == q->val) //加一个判断
                q = q->next;
            ListNode *res = deleteDuplicates(q);
            head->next = res;
            return head;
        }
    

    Leetcode #19 删除链表倒数第n个结点

    题名:删除链表倒数第n个结点
    描述:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

    进阶:你能尝试使用一趟扫描实现吗?

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

    方法一:

    需要扫描2遍链表,第一遍是计算出链表总长度m,令ltf = m - n 再从头扫描到ltf处,删除该位置的结点即可。链表操作一般引入一个空头结点会比较方便。
    

    代码如下:

    
        ListNode* removeNthFromEnd(ListNode* head, int n) {
            ListNode hh(-1);//引入一个头结点;在栈空间中,退出该函数后会自动销毁;如果要new一个则在堆空间中,记得最后要自己释放该空间!
            hh.next = head;
            ListNode *p = head;
            int count = 0;
            while(p){
                count++;
                p = p->next;
            }
            p = head;
            ListNode *pre = &hh;
            int lft = count - n;
            while(lft--){
                pre = p;
                p = p->next;
            }
            pre->next = p->next;
            return hh.next;
        }
    

    方法二:一次扫描

    如何做到一次扫描就能删除倒数第n个结点呢?可以将扫描时得到的各节点的地址记录到数组中,因为数字索引是O(1)的,故而可以做到一次扫描就完成。
    代码如下:

    
        ListNode* removeNthFromEnd(ListNode* head, int n) {
            vector<ListNode *> vi;
            ListNode hh(-1);
            hh.next = head;
            vi.push_back(&hh);
            ListNode *p = head;
            while(p){
                vi.push_back(p);
                p = p->next;
            }
            vi[vi.size() - n - 1]->next = vi[vi.size() - n]->next;
            return hh.next;
        }
    

    Leetcode #24 两两交换链表中的结点

    题名:两两交换链表中的结点
    描述:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

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

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/swap-nodes-in-pairs/

    方法:

    同样的,引入一个空头结点将简化很多思想和操作。要考虑链表中元素个数为奇数个的情况,此时最后一个结点无需交换。
    代码如下:

    
        ListNode* swapPairs(ListNode* head) {
            if(head == NULL || head->next == NULL)
                return head;
            ListNode hh(-1);
            ListNode *p = head, *q = head->next, *f = &hh;
            while(p && q){
                //需要四个指针: f,p,q,next;用于完成交换
                ListNode *next = q->next;
                f->next = q;
                q->next = p;
                p->next = next;
                //更新f,p,q
                f = p;
                p = next;
                if(p)    q = p->next;//考虑p是空指针的情况
            }
            return hh.next;
        }
    

    Leetcode #328 奇偶链表

    题名:奇偶链表
    描述:给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

    请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/odd-even-linked-list/

    方法:

    又是一题List + Simulation的题,按照题意进行模拟即可。我们分成奇偶两个链表同时进行。

    
        ListNode* oddEvenList(ListNode* head) {
            if(!head || !head->next)    return head;//如果是空链表或链表结点数小于2,不需要操作直接返回;
            ListNode *oddp = head, *evenp = head->next, *p = head, *op, *ep;//oddp和evenp分别指向奇偶链表的表头;op和ep用于移动;
            while(p && p->next){//因为是对奇偶链表同时进行,所以要往后多看一个结点是否为空
                op = p;
                ep = p->next;
                p = p->next->next;//p指向下一次while开始处
                if(ep->next){//如果ep的下一个结点也非空
                    op->next = ep->next;//将结点插入奇数链表
                    if(ep->next->next){//如果ep的下下个结点非空
                        ep->next = ep->next->next;//插入偶数链表
                    }
                }      
            }
            if(!p)  op->next = evenp;//这里要分情况;如果输入链表结点个数为偶数个,那么从while出来p一定是NULL
            else//如果输入链表结点个数为奇数个,那么从while出来p->next一定是NULL,而p是非空
                op->next->next = evenp;//将奇数链表的最后一个结点的next指针指向偶数链表表头,以串联起奇偶链表;
            ep->next = NULL;//令偶链表,表尾指向NULL;
            return head;
        }
    
  • 相关阅读:
    windows中dos命令指南
    HDU 2084 数塔 (dp)
    HDU 1176 免费馅饼 (dp)
    HDU 1004 Let the Balloon Rise (map)
    变态杀人狂 (数学)
    HDU 2717 Catch That Cow (深搜)
    HDU 1234 开门人和关门人 (模拟)
    HDU 1070 Milk (模拟)
    HDU 1175 连连看 (深搜+剪枝)
    HDU 1159 Common Subsequence (dp)
  • 原文地址:https://www.cnblogs.com/Codroc/p/12340821.html
Copyright © 2011-2022 走看看