赞
踩
前言
上一篇讲解了链表的基本操作详解,接下来练习一下链表的应用。
目录
移除链表元素习题链接: 移除链表元素
实现在链表中删除所有给定的key
1.首先先遍历链表
2.如果有某个节点的val值等于vag,那么让这个节点的上一个节点next存放该节点的next(相当于就是跳过了那个相等的节点,跳过了相当于就是删除了)
3.然后继续遍历找下一个相同的,找到进行同样的操作。
4.最后解决头部问题,对链表的头的val与val进行比较,如果同则让头指向下一个节点即可。
代码实现
- public ListNode removeElements(ListNode head, int val) {
- //看看是不是空的
- if(head == null){
- return null;
- }
-
- //定义两个节点指针,指向头另一个指向头的下一个
- ListNode pevr = head;
- ListNode cur = head.next;
-
- while(cur != null){
- //2.如果有某个节点的val值等于vag,那么让这个节点的上一个节点next存放该节点的next
- // (相当于就是跳过了那个相等的节点,跳过了相当于就是删除了)
- if(cur.val == val){
- pevr.next = cur.next;
- cur = cur.next;
-
-
- }else{
- pevr = cur;
- cur = cur.next;
- }
- }
- //最后解决头部问题,对链表的头的val与val进行比较,如果同则让头指向下一个节点即可。
- if(head.val == val){
- head = head.next;
- }
- return head;
-
- }

反转链表链接:反转链表
思路分析
反转一个链表是需要改变其链表结构的,
翻转链表,就是让第二个节点开始,依次进行头插
要做到这一步,需要设置两个指针,一个指向头节点的下一个节点
然后另一个指针就是保留这个节点的下一个节点,让第一个指针节点进行头插后,
再回来还能找到下一个要插入的节点地址,然后头结点变成插入好的节点,就这样遍历一下链表即可反转完成。
步骤
1.遍历链表
2.进行头插,改变头结点
3.继续下一个节点的头插,重复遍历完即可。
反转前
反转后
//翻转链表,就是让第二个节点开始,依次进行头插 public ListNode overturn(){ if (head ==null){ return null; } //如果是一个结点直接 if (head.next ==null){ return head; } //改变链表内部结构 ListNode cur = head.next; head.next = null; while(cur != null){ //1.循环条件咋么设置,还是cur != null,因为最后一个是null // 头插能实现第一个,但是后面的2接上呢,让cur返回来就行了 ListNode curNode = cur.next; cur.next = head; head = cur; cur = curNode;//当做指针 } return head; }分析
ListNode curNode = cur.next; cur.next = head; head = cur; cur = curNode;//当做指针
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结 点。
习题链接:链表中间结点
利用两个指针,一个是慢一个快,快的是一次走两步
原理就是路程相同为800米的田径场跑道,A同学和B同学同时起跑,A同学是B同学的速度的2倍,那么当A到达终点,B肯定在中点。
1.创建两个指针同时指向头结点;
2.然后遍历一遍,注意循环结束的条件是判断快指针是否到达终点;
3.走完一遍,返回慢指针即是中间结点。
第一步
第二步
第三步
此时当fast到达末尾,那么slow必然在中间位置。
- //快慢,速度为2倍,同样的起点,终点一样,速度是2倍
- public ListNode middleNode(ListNode head){
- ListNode fast = head;
- ListNode slow = head;
- while( fast != null && fast.next !=null )//fast两倍速度会提前一半到终点
- //奇数情况fast.next ==null,偶数情况fast == null
- //这里需要注意,这两个条件不能换过来。
- {
-
- fast = fast.next.next;//2倍速度走两步
- slow = slow.next;//慢的走一步
-
- }
- //走完说明slow在中间
- return slow;
-
- }

习题链接:链表中倒数最后k个结点_牛客题霸_牛客网 (nowcoder.com)
题目描述:输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
第一解决办法
比如要返回倒数第2个结点,那么直接用链表的 总长度 - 2,得到的数就是头结点要走的长度。
走完,返回fast即可。
代码实现
ListNode fast = head; int count = 0; while(fast != null){ count++; fast = fast.next; } //限制一下k范围 if(head == null || k < 0 || k >count){ return null; } fast = head; //重置为头位置 int len = count - k; // while(len > 0){ fast = fast.next; len--; } return fast;
思路分析
输入一个链表,输出该链表中倒数第k个结点。 1.fast 先走k-1步 2.然后slow和fast同时走 3.fast到最后slow的位置就是倒数k-1 需要注意的是要对k进行判断是否合法 还需要解决当链表为空或者fast指向了空的情况原理fast和slow永远相差k-1步
画图分析
代码实现
- //限制k值和防止空表
- if ( k <= 0 || head == null ) {
- return null;
- }
- ListNode fast = head;
- ListNode slow = head;
-
- //怎么先让fast先走k-1步
- for (int i = 0 ; i < k - 1 ; i ++) {
-
- fast = fast.next;//如果k>size()那么fast就指向null了
- if (fast == null) { //处理k太大问题
- return null;
- }
- }
- //同时走
- //奇偶情况是如何的呢,通过画图分析是一样的
- while (fast.next != null) {
-
- fast = fast.next;
- slow = slow.next;
-
- }
- //走完了然后勒
- //走完就说明slow就是要求的位置
- System.out.println(slow.val);
- return slow;

习题链接:21. 合并两个有序链表 - 力扣(LeetCode)
题目描述:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
合并两个链表,要改变其结构
1.创建好一个新的链表的头结点,相当于一辆火车先有车头
2.然后让两个链表的头结点分别进行比较大小,小的先在新的链表里面进行尾插,然后头结点更换到下一个,然后继续比较大小。
3.最后比较完成,拼接完成新链表,返回新链表的头结点即可。
- public ListNode mergeTwoLists(ListNode head1, ListNode head2) {
- //1.定义虚拟的字节,作为新链表头结点
- ListNode newH = new ListNode(-1);
- ListNode tmpH = newH;
- //2.遍历进行比较大小
- while(head1 != null && head2 != null){
- if(head1.val < head2.val){
-
- tmpH.next = head1; //进行尾插到新的链表中
- head1 = head1.next; //头结点改变下一个。
-
- }else{
-
- tmpH.next = head2;
- head2 = head2.next;
-
-
- }
- tmpH = tmpH.next;//指向末尾
- }
- //可能两个链表长度不一样,出了循环进行检查
- if(head1 != null){
- tmpH.next = head1;
- }
- if(head2 != null){
- tmpH.next = head2;
- }
- //3.返回新链表头结点
-
- return newH.next;
- }

习题链接:链表分割_牛客题霸_牛客网 (nowcoder.com)
题目描述:现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
链表的分割,要分割的话就要分两个区间,一个存放小于x的区间,另一个则是大于x的。
1.那么就有先创建四个指针分别为第一第二区间链表的头尾指针。
2.然后对链表进行一次遍历,每个结点的值依次与x比较,然后放到对应的区间。
3.遍历完,然后进行连接成一个新的链表,再返回新链表的头结点。
需要注意的是两种情况,如果第二区间是空的则无需连接
还有就是如果第一个区间为空,那么直接返回第二区间的头结点
最后就是要手动给第二区间的最后一个结点的next置为null不然会报错异常。
第一步,第一个结点12小于35,因此放在第一个区间be、bs这时都指向第一个结点
第二步,第二个结点值是5也小于35,放第一个区间,然后尾指针be指向他。
第三步,第三个结点值大于35,放到第二区间,后续遍历也如此,这里就不一一列出。
最后结果
- //会改变链表的结构
- public ListNode partition(ListNode head, int x) {
- // write code here
-
- ListNode cur = head;
-
- ListNode bs = null;//创建好节点类型的用于保存分割后两个区间的节点位置地址
- ListNode be = null;
-
- ListNode as = null;
- ListNode ae = null;
-
- while(cur != null){//遍历
- //分割
- if(cur.val < x ){
- if(bs == null){//如果是第一个头尾都指向他
- bs = cur;
- be = cur;
-
- }else{
- //如果是第二个以后进行尾插
- be.next = cur;//尾插原理
- be = be.next;
- }
-
- }else{
- if(as == null){
- as = cur;
- ae = cur;
- }else{
- ae.next = cur;
- ae = ae.next;
- }
-
- }
- cur = cur.next;
- }
- //有一种情况,如果链表是3333,x为3,那么前面区间是空的
- //此时be.next会空指针异常
- //因此要对第一区间进行判断
- if(be == null){
- return as;//如果第一区间是空那么没必要连接,返回第二区间的头即可。
- }
-
- //连接起来
- //用第一个区间的尾和第二个区间的头进行连接
- be.next = as;
-
- //还有一个情况,就是拼接起来后,第二区间的尾节点的next有可能不为空,异常
- if(as != null ){//此时需要手动进行置空
- //第二区间没有数据情况
- ae.next = null;
- }
- return bs;//返回第一区间的头
-
- }

习题链接:链表的回文结构_牛客题霸_牛客网 (nowcoder.com)
题目描述:
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
要判断是否是回文,那么就对前后两边进行比较,如果相同两边同时走一步,如果不是同则返回false,然后继续判断,直到结束,能走到结束说明是回文结构。
那么要实现上面的效果,首先就得需要改变链表后半部分的结构。
1.首先要用快慢指针找到中间位置,然后翻转中间后面节点(使用上面的翻转思路)。
2.翻转完成,此时slow处于链表末尾,让头head和尾slow同时往中间走,每走一步进行比较val内容是否相同,然后继续下一步,直到循环结束。
3.走完返回true。
需要注意的是需要区分奇数个节点和偶数个节点的区别就行。
1.快慢指针取中间
2.翻转后面节点
3.前后往中走
4.解决偶数问题
- public boolean chkPalindrome(ListNode head) {
- if(head == null || head.next == null){
- return true;
- }
- // write code here
- //中间位置
- ListNode fast = head;
- ListNode slow = head;
- while(fast != null && fast.next != null ){//这个顺序不能改啊,因为会出现空指针异常
- fast = fast.next.next;//走两步
- slow = slow.next;
- }
- //走完即可得到中间位置
-
- //从中间开始翻转
- //定义两个指向slow的后面,把slow当做头,然后进行翻转
- ListNode cur = slow.next;
-
-
- while(cur != null){
- ListNode curNext = cur.next;//提前存好cur的下一个地址,不然丢失
- cur.next = slow;//slow后面指向存放slow地址
- slow = cur; //然后slow相当于头结点往后走
- cur = curNext;//然后继续下一个节点翻转
-
- }
-
- //走到这里翻转完成,此时slow指向最后一个节点位置
- //3.进行前后移动
- //fast = head; //让fast回到开头
- //此时fast和slow一个在头一个在尾,进行移动即可。
- //可是怎么知道走到中间了呢
- while(head != slow){
- //因为后面被翻转了,所以中间的左右节点的next放的地址都是中间的地址。
- if(head.val != slow.val){//进行值的对比,如果有一个不对就不是回文
- return false;
-
- }
- //解决偶数的情况
- if(head.next == slow){
- return true;
- }
- //这三步骤得按顺序来
- head = head.next;
- slow = slow.next;
-
- }
- return true;
-
- }

题目描述:给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
1.分别求长度
2.求两个长度差值,让长的先走完差值步数
3.然后同时走
4.当他们相遇的时候那个相遇点就是相交节点
- public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
- // 忽略了一点,这俩链表不一定都是在第n个节点相交,所以要让长度长的提前走几步
- int len1 = getLen(headA);
- int len2 = getLen(headB);
-
- // 俩指针分别指向俩链表,如果某一个指针到达尾部还没有匹配到相交点,则无交点
- ListNode fast = headA;
- ListNode slow = headB;
-
- //让长的走差值步
- if (len1 > len2) {
- for (int i = 0; i < len1 - len2; i++) {
- fast = fast.next;
- }
- } else if (len2 > len1) {
- for (int i = 0; i < len2 - len1; i++) {
- slow = slow.next;
- }
- }
-
- //“同步进度之后同时走再找交点”
- while (fast != null && slow != null) {
- if (fast == slow)
- return fast;
- fast = fast.next;
- slow = slow.next;
- }
- return null;
- }
-
- int getLen(ListNode head) {
- ListNode cur = new ListNode();
- cur.next = head;
- int res = 0;
- while (cur.next != null) {
- res++;
- cur = cur.next;
- }
- return res;
- }

题目描述:
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
原理可以理解为追击问题
一个田径场圈,两人速度相差一倍,一定会有相遇的时候,相遇就是有环。
1.利用快慢指针,一个走两步,一个走一步
2.如果有环,那么两者肯定会相遇,每走完一步就进行判断
- public boolean hasCycle(ListNode head) {
- ListNode fast = head;
- ListNode slow = head;
-
- while(fast != null && fast.next != null){
- fast = fast.next.next; //走两步
- slow = slow.next;
- if(fast == slow){//每走完一步就进行判断
- return true;
- }
- }
- return false;
-
- }
题目链接:142. 环形链表 II - 力扣(LeetCode)
题目描述:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL
不允许修改 链表。
找入环点。
让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针 都是每次均走一步,最终肯定会在入口点的位置相遇。
- public ListNode detectCycle(ListNode head) {
- ListNode fast = head;
- ListNode slow = head;
-
- while(fast != null && fast.next != null){
- fast = fast.next.next; //走两步
- slow = slow.next;
- if(fast == slow){//每走完一步就进行判断
- break;
- }
- }
- if(fast ==null || fast.next == null){
- return null;
- }
-
- //直接让慢指针从头开始走
- //让fast在相遇点那里走,再次相遇就是入口点。
- slow = head;
- while(slow != fast){
- fast = fast.next;
- slow = slow.next;
- }
- return slow;
- }

本篇主要对链表的习题进行讲解,如果有疑惑的或者讲解的地方有问题,可以在评论区交流谢谢。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。