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;
        }
    
  • 相关阅读:
    网络编程
    网络参考模型
    面向对象中类的成员
    Forward(请求转发)和Redirect(重定向)的比较
    AOP详解
    JDK动态代理和CGLib代理对比
    内存可见性问题分析
    Java中创建对象的5种方式
    基于Spring的动态路由AbstractRoutingDataSource实现动态分库
    在java中String类为什么要设计成final?
  • 原文地址:https://www.cnblogs.com/Codroc/p/12340821.html
Copyright © 2011-2022 走看看