判环算法以及链表常见算法题

  由于涉及到Floyd判环算法,故先简单阐述一下Floyd判环算法原理。

Floyd判环算法

算法原理:设置两个指针同时从头结点向后访问(当两指针后继不为null),不过两指针的访问速度不一致,第一个指针每次后移一个元素,第二个指针每次后移两个元素。若链表成环,则两指针在某一时刻必然相遇,否则不相遇

计算环长度:
当指针相遇时,保持其中一个指针不动,另一个指针向后访问,每次后移一个单位,当两指针再次相遇时,该指针移动的单位即为环的长度

计算环起始节点:
第一次相遇后,慢指针从头结点向后访问,快指针从相遇点同时向后访问,不过两者的移动速度都为1,当两者再次相遇时,该相遇的节点即为环的起始节点,分析如下:

如下图所示,假设h为链表头结点,p为两指针第一次相遇节点,m为头结点到起始节点走过的路程(经过的节点个数),n为环起始节点到p的路程,则对于第一次相遇有:

慢指针走过的路程:s=m+n+aC

快指针走过的路程:2
s=m+n+bC

a,b分别表示两指针相遇前第一次经过p点后绕环的圈数,C表示环的长度,由于快指针的速度是慢指针的两倍,时间一样,故快指针走过的路程是慢指针的两倍。

两式相减有s=(b-a)
C=m+n+a*C

故m+n恰为环长度的整数倍

慢指针从头结点到起始节点走了m,由于同时同速度快指针也走了m,而移动前快指针距起始节点距离为n,m+n恰为环长度的整数倍,故快指针移动m后也到了起始节点,即此时相遇点为起始节点


1.寻找链表的第n个节点

/**
 * 时间复杂度为O(n),空间复杂度为O(1)
 * @param head
 * @param kth
 * @return
 */
public static SNode getKthNode(SNode head,int kth){
    SNode temNode=head,kthNode=null;
    for(int count=1;count<kth;count++) {
        if(temNode!=null)
            temNode=temNode.getNext();
    }
    while(temNode!=null) {
        if(kthNode==null)
            kthNode=head;
        else
            kthNode=kthNode.getNext();
        temNode=temNode.getNext();
    }
    if(kthNode!=null)
        return kthNode;
    return null;
}

2.1判断链表是否以null结尾,是否包含环

/**
 * 时间复杂度为O(n),空间复杂度为O(1)
 * @param head
 * @return
 */
public static boolean doesLinkedListContainsLoop(SNode head) {
    if(head==null)
        return false;
    SNode fastNode=head,slowNode=head;
    while(fastNode.getNext()!=null&&fastNode.getNext().getNext()!=null) {
        slowNode=slowNode.getNext();
        fastNode=fastNode.getNext().getNext();
        if(fastNode==slowNode)
            return true;
    }
    return false;
}

2.2判断给定的链表是否以null结束。如果链表中存在环,找到环的起始节点

/**
 * @param head
 * @return
 */
public static SNode findBeginOfLoop(SNode head) {
    SNode slowNode=head,fastNode=head;
    boolean loopExists=false;
    if(head==null)
        return null;
    while(fastNode.getNext()!=null&&fastNode.getNext().getNext()!=null) {
        slowNode=slowNode.getNext();
        fastNode=fastNode.getNext().getNext();
        if(slowNode==fastNode) {
            loopExists=true;
            break;
        }
    }
    if(loopExists) {  //环存在
        slowNode=head;
        while(slowNode!=fastNode) {
            slowNode=slowNode.getNext();
            fastNode=fastNode.getNext();
        }
        return slowNode;  //返回环开始节点
    }
    return null;  //环不存在
}

2.3判断链表是否存在环,若存在,则返回环的长度,否则返回0

/**
 * @param head
 * @return
 */
public static int findLoopLength(SNode head) {
    SNode slowNode=head,fastNode=head;
    boolean loopExists=false;
    int counter=0;
    if(head==null)
        return 0;
    while(fastNode.getNext()!=null&&fastNode.getNext().getNext()!=null) {
        slowNode=slowNode.getNext();
                    counter++;
        fastNode=fastNode.getNext().getNext();
        if(slowNode==fastNode) {
            loopExists=true;
            break;
        }
    }
    if(loopExists) {
        fastNode=fastNode.getNext();
        while(fastNode!=slowNode) {
            fastNode=fastNode.getNext();
            counter++;
        }
        return counter;
    }
    return 0;
}

3.将一个循环链表变成两个循环链表

/**
 * @param head
 * @param head1
 * @param head2
 */
public static void splitList(SNode head,SNode head1,SNode head2) {
    SNode slowNode=head,fastNode=head;
    if(head==null)
        return;
    //如果循环链表有奇数个节点,则fastNode.getNext()指向head
    //如果循环链表有偶数个节点,则fastNode.getNext().getNext()指向head
    while(fastNode.getNext()!=head&&fastNode.getNext().getNext()!=head) {
        fastNode=fastNode.getNext().getNext();
        slowNode=slowNode.getNext();
    }
    if(fastNode.getNext().getNext()==head) {
        fastNode=fastNode.getNext();
    }
    //head1指向前半部分的head指针
    head1=head;
    //head2指向后半部分的指针
    if(head.getNext()!=head)
        head2=slowNode.getNext();
    //把后半部分变成环
    fastNode.setNext(slowNode.getNext());
    //把前半部分变成环
    slowNode.setNext(head);
}

4.有序链表中插入一个节点

/**
 * 时间复杂度为O(n),空间复杂度为O(1)
 * @param head
 * @param newNode
 * @return
 */
public static SNode insertSortedList(SNode head,SNode newNode) {
    SNode current=head,temp=null;
    if(head==null)
        return newNode;
    while(current!=null) {
        temp=current;
        current=current.getNext();
    }
    newNode.setNext(current);
    temp.setNext(newNode);
    return head;
}

5.1链表逆置

/**
 * 时间复杂度为O(n),空间复杂度为O(1)
 * @param head
 * @return
 */
public static SNode reverseList(SNode head) {
    SNode temp=null,nextNode=null;
    while(head!=null) {
        nextNode=head.getNext();
        head.setNext(temp);
        temp=head;
        head=nextNode;
    }
    return temp;
}

5.2逐对逆置链表,假如初始链表为1 2 3 4 x,逐对逆置后为2 1 4 3 x

/**
 * @param head
 * @return
 */
public static SNode reversePairRecursive(SNode head) {
    SNode temp;
    if(head==null||head.getNext()==null)
        return head;
    else {
        temp=head.getNext();
        head.setNext(temp.getNext());
        temp.setNext(head);
        head=temp;
        head.getNext().setNext(reversePairRecursive(head.getNext().getNext()));
        return head;
    }
}

6.判断两个链表是否相交成为一个单链表,若相交,得出两链表的相交节点

/**
 * 思路:两链表相交之后的长度必然相等,先获得两链表的长度,让更长的链表先后移两链表长度差个单位,再一起向后遍历,直到出现两节点相等或到达表尾
 * 时间复杂度为O(m,n),空间复杂度为O(1)
 * @param list1
 * @param list2
 * @return
 */
public static SNode findIntersectingNode(SNode list1,SNode list2) {
    int l1=0,l2=0,diff=0;
    SNode head1=list1,head2=list2;
    while(head1!=null) {
        l1++;
        head1=head1.getNext();
    }
    while(head2!=null) {
        l2++;
        head2=head2.getNext();
    }
    if(l1<l2) {
        head1=list2;
        head2=list1;
        diff=l2-l1;
    }else {
        head1=list1;
        head2=list2;
        diff=l1-l2;
    }
    for(int i=0;i<diff;i++)
        head1=head1.getNext();
    while(head1!=null&&head2!=null) {
        if(head1==head2)
            return head1;
        head1=head1.getNext();
        head2=head2.getNext();
    }
    return null;
}

7.寻找链表的中间节点:设置两个指针,第一个指针的移动速度是第二个指针的两倍,当第一个指针到达表尾,第二个指针即指向中间节点

/**
 * 时间复杂度为O(n),空间复杂度为O(1)
 * @param head
 * @return
 */
public static SNode findMiddle(SNode head) {
    SNode point1=head,point2=head;
    int i=0;
    while(point1.getNext()!=null) {
        if(i==0) {
            point1=point1.getNext();
            i=1;
        }else if(i==1) {
            point1=point1.getNext();
            point2=point2.getNext();
            i=0;
        }
    }
    return point2;
}

8.检查链表长度是奇数还是偶数,每次后移两个元素,返回1表示奇数,返回0表示偶数

/**
 * 时间复杂度为O(n/2),空间复杂度为O(1)
 * @param head
 * @return
 */
public static int isLinkedListLengthEven(SNode head) {
    while(head!=null&&head.getNext()!=null) {
        head=head.getNext().getNext();
    }
    if(head==null)
        return 0;
    else
        return 1;
}

9.合并两个有序链表

/**
 * 时间复杂度为O(n)
 * @param a
 * @param b
 * @return
 */
public static SNode mergeList(SNode a,SNode b) {
    SNode result=null;
    if(a==null)
        return b;
    if(b==null)
        return a;
    if(a.getData()<=b.getData()) {
        result=a;
        result.setNext(mergeList(a.getNext(), b));
    }else {
        result=b;
        result.setNext(mergeList(a,b.getNext()));
    }
    return result;
}

10.从表尾开始输出链表

/**
 * @param head
 */
public static void printListFromEnd(SNode head) {
    if(head==null)
        return;
    printListFromEnd(head.getNext());
    System.out.println(head.getData());
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,277评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,777评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,946评论 0 245
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,271评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,636评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,767评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,989评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,733评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,457评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,674评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,155评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,518评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,160评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,114评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,898评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,822评论 2 280
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,705评论 2 273

推荐阅读更多精彩内容

  • //leetcode中还有花样链表题,这里几个例子,冰山一角 求单链表中结点的个数----时间复杂度O(n)这是最...
    暗黑破坏球嘿哈阅读 1,471评论 0 6
  • 转载请注明出处:http://www.jianshu.com/p/c65d9d753c31 在上一篇博客《数据结构...
    Alent阅读 3,448评论 4 74
  • B树的定义 一棵m阶的B树满足下列条件: 树中每个结点至多有m个孩子。 除根结点和叶子结点外,其它每个结点至少有m...
    文档随手记阅读 13,017评论 0 25
  • 本来以为一篇就能写完的,后来又感觉一篇多了一些,所以关于链表的简单算法题有加了个续篇,和上一篇一样,难度不会太大。...
    zero_sr阅读 528评论 0 4
  • 大学的时候不好好学习,老师在讲台上讲课,自己在以为老师看不到的座位看小说,现在用到了老师讲的知识,只能自己看书查资...
    和珏猫阅读 1,387评论 1 3