zoukankan      html  css  js  c++  java
  • 链表系列文章(二)

    上一篇文章实现了链表的基本数据结构,这一篇谈一谈关于链表的一些有趣的题目

    首先给出链表节点的数据结构

    1 struct ListNode {
    2     int var;
    3     ListNode *next;
    4     ListNode(int v = 0 ) : var(v), next(NULL) {  }
    5 };

    1. 删除链表的倒数第K个节点

    例如:给定链表:1->2->3->4->5, n = 5, 返回2->3->4->5

    方法一:常规做法,两次扫描,第一次扫描得到链表的长度,然后求得待删除节点的正向索引,第二次扫描删除节点

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

     1 ListNode *removeNthNodeFromEndOfList(ListNode *head, int k) {
     2     if( !head || k < 1 ) return NULL;
     3     int len = 0;
     4     ListNode *p = head, newhead(-1);
     5     newhead.next = p;
     6     for( ; p; p = p->next ) len++;//第一遍扫描,得到链表长度
     7     k = len - k;//得到正向索引的前一个节点的索引
     8     if(k < 0) return NULL;
     9     p = &newhead;
    10     while( k-- ) p = p->next;//第二遍扫描,定位待删除节点的前一个节点
    11     
    12     ListNode *tmp = p->next;
    13     p->next = tmp->next;
    14     delete tmp;
    15     return newhead.next;
    16 }

    方法二:两个指针, 第一个指针先走K步,然后两个指针同时走,当第二个指针走到链表尾时,第一个指针指向的节点就是要删除的节点

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

     1 ListNode *removeNthNodeFromEndOfList(ListNode *head, int k) {
     2     if(!head || k < 1)    return NULL;
     3     ListNode newhead(-1), *p = &newhead, *q = &newhead;
     4     newhead.next = head;      
     5     while( k-- && q ) q = q->next; //q->next指向正向第k个节点
     6     if( !q ) return NULL;
     7     while( p && q->next ) { p = p->next; q = q->next; }//p->next就是要删除的节点
     8     q = p->next;
     9     p->next = q->next;
    10     delete q;
    11     return newhead.next;
    12 }

    2.  复制带随机指针的链表,随机指针要么指向链表中的节点要么为空

    1 struct RandomListNode {
    2     int var;
    3     RandomListNode *next, *random;
    4     RandomListNode(int v) : var(v), next(NULL), random(NULL) { }
    5 };

    方法一:常规做法,先扫面一遍链表复制next指针;然后对于每个节点,定位随机节点在链表中的索引,然后复制每个random节点时,通过扫描next指针

    得到定位索引对应的节点,将random指针指向该节点

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

     1 RandomListNode *copyRandomList(RandomListNode *head) {
     2     if( !head ) return NULL;
     3     int n = 0;
     4     RandomListNode *p = head, newhead(-1), r = &newhead;
     5     for( ; p; p = p->next ) {//扫描链表,复制next指针,同时记录链表长度
     6         r->next = new RandomListNode(p->var);
     7         r = r->next; n++;
     8     }
     9     r = &newhead;
    10     for(p = head; p; p = p->next) {//循环复制random指针
    11         if(NULL == p->random)
    12             r->next->random = NULL;
    13         else if(p->random == p)
    14             r->next->random = r->next;
    15         else {//指向链表中的其它节点
    16             int k = n;
    17             RandomListNode *q = p->random;
    18             RandomListNode *h = newhead.next;
    19             while(q) {k--; q = q->next;}//random在源链表中的正向索引
    20             while(k--) {h = h->next;}//在复制链表中找到该节点
    21             r->next->random = h;
    22         }
    23         r = r->next;
    24     }
    25     return newhead.next;
    26 }

    方法二:将复制链表的节点一一插入到源链表的对应节点后:

    如源链表(只显示next指针):s1->s2->s3->s4

    插入后:s1->d1->s2->d2->s3->d3->s4->d4,比如要复制d1的random指针,只需d1->random = s1->random->next即可

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

     1 RandomList *copyRandomList(RandomListNode *head) {
     2     if( !head ) return NULL;
     3     RandomListNode newhead(-1);
     4     for( RandomListNode* p = head; p != NULL ) { //一一插入复制链表节点到对应源链表节点后
     5         RandomListNode* q = new RandomListNode(p->var);
     6         q->next = p->next;
     7         p->next = q;
     8         p = q->next;
     9     }
    10     for( RandomListNode* p = head; p != NULL ) {//复制random指针
    11         if (p->random != NULL)
    12             p->next->random = p->random->next;
    13         p = p->next->next;
    14     }
    15     for( RandomListNode* p = head, *d_p = &newhead; p != NULL ) {//恢复两个链表
    16         d_p->next = p->next;
    17         d_p = d_p->next;
    18         p->next = p->next->next;
    19         p = p->next;
    20     }
    21     return newhead.next;
    22 }

    3. 判断链表是否有环,如果有,找到交点

    判断有环:

    方法一:hash,用hash表记录每个节点的地址,当第一次遇到hash值存在时,表明相交,同时找到了第一个节点,否则如果走到空,则无交点

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

    方法二:快慢指针,fast指针每次走两步,slow指针每次走一步,因此fast指针相对slow指针每次走一个,如果有环,必然在有限步相交,至于判断交点,参考博文:

                 http://www.cnblogs.com/carlsama/p/4127201.html

    由于本人水平有限,文章中难免有不让和错误之处,欢迎大家批评指正,愿共同进步!!!

  • 相关阅读:
    jdk环境变量配置
    Oracle常用操作命令
    Oracle数据库突然宕机,处理方案
    Oracle重启操作步骤
    大家好,我是一名程序员,这就是2017年的我……
    Oracle恢复删除数据
    网络爬虫
    第一篇博客
    函数(二)
    MFC(二)
  • 原文地址:https://www.cnblogs.com/zhuoyuan/p/4130049.html
Copyright © 2011-2022 走看看