查找单链表的中间节点,要求只能遍历一次链表
在只能遍历一遍链表的基础上我们需要查找到中间节点,则需
设置一个快慢节点fast&slow,当我们每次只让慢节点走一步
快节点走两步,则当快节点为空时,则慢节点即为要查找的中间节点。
- 1
- 2
- 3
package com.struct.interview_question.list_interview_question.findmiddlenode;
import com.struct.interview_question.list_interview_question.ListNode;
/**
*@Description: 查找链表的中间节点,只遍历一次链表
*@Author: dyy
*/
public class FindMiddleNode {
public ListNode findMiddleNode(ListNode head){
if(head==null||head.nextNode==null){
return head;
}
//走的慢的节点每次走一步
ListNode slowNode = head;
//走的块的节点每次走两步
ListNode fastNode = head;
//当走的快的节点为null时,此时走的慢的节点即为中间节点
while(fastNode!=null && fastNode.nextNode!=null){
slowNode = slowNode.nextNode;
fastNode = fastNode.nextNode.nextNode;
}
return slowNode;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
@Test
public void test_FindMiddleNode(){
ListNode head = new ListNode("lemon");
head.nextNode = new ListNode("lll");
head.nextNode.nextNode = new ListNode(1);
head.nextNode.nextNode.nextNode = new ListNode('a');
head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
FindMiddleNode findMiddleNode = new FindMiddleNode();
ListNode node = findMiddleNode.findMiddleNode(head);
System.out.println(node.data);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
查找单链表的倒数第k个节点
方案一:我们按照正常的思路,遍历链表,记录链表的总长度
当我们知道链表的总长度count后,直接输出链表的count-k个元素即可
- 1
- 2
package com.struct.interview_question.list_interview_question.findmiddlenode;
import com.struct.interview_question.list_interview_question.ListNode;
public class FindCountdownKNode {
//查找倒数第index个节点(遍历两遍链表)
public ListNode findKNode(ListNode head,int index){
if(head == null||index==0) {
return null;
}
int count = 0;
ListNode cur = head;
while(cur!=null){
count++;
cur = cur.nextNode;
}
if(index>count){
System.out.println("指定值大于链表长度");
}
ListNode node = head;
for(int i = 0;i < count-index;i++){
node = node.nextNode;
}
return node;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
方案二:在之前只遍历一遍找到中间节点的基础上,我们可以
令一个走的快的节点fast和一个走的慢的节点slow,使ast先走
k-1步,若fast为空则表明k大于链表的总长度,输出null,
紧接着,我们可以使fast和slow同时走,当fast.next为空时,slow即为所求的倒数第k个节点
- 1
- 2
- 3
- 4
package com.struct.interview_question.list_interview_question.findmiddlenode;
import com.struct.interview_question.list_interview_question.ListNode;
public ListNode FindKNodeSimple(ListNode head,int index){
if(head==null||index==0){
return null;
}
ListNode slow = head;
ListNode fast = head;
for(int i = 0;i < index-1;i++){
fast = fast.nextNode;
//此处表明index已经大于链表长度
if(fast==null){
return null;
}
}
while (fast.nextNode!=null){
slow = slow.nextNode;
fast = fast.nextNode;
}
return slow;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
@Test
public void test_FindCountdownKNode() {
ListNode head = new ListNode("lemon");
head.nextNode = new ListNode("lll");
head.nextNode.nextNode = new ListNode(1);
head.nextNode.nextNode.nextNode = new ListNode('a');
head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
FindCountdownKNode findCountdownKNode = new FindCountdownKNode();
ListNode node = findCountdownKNode.findKNode(head, 3);
System.out.println(node.data);
ListNode node1 = findCountdownKNode.FindKNodeSimple(head, 6);
System.out.println(node1);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
删除链表的倒数第K个结点
(1)当链表的长度刚好等于n时,则要删除的节点刚好是头结点,
(2)链表的长度大于n时,我们使前者节点premer和后者节点latter
同时向后移动,知道premer.next为null时,则latter节点刚还为
要删除节点的前一个节点,我们可以通过改变指向来进行删除。
- 1
- 2
- 3
- 4
package com.struct.interview_question.list_interview_question.deletenode;
import com.struct.interview_question.list_interview_question.ListNode;
/**
*@Description: 删除倒数第k个节点
*@Author: dyy
*/
public class DeleteCountdownKNode {
//要删除倒数第index个节点
public boolean deleteCountdownNode(ListNode head,int index){
if(index==0||head==null) return false;
//走在前面的节点
ListNode premer = head;
//走在后面的节点
ListNode latter = head;
for(int i = 0; i < index; i++){
premer = premer.nextNode;
}
//此处表明要删除节点刚好为头结点
if(premer == null){
head = head.nextNode;
return true;
}
while (premer.nextNode!=null){
premer = premer.nextNode;
latter = latter.nextNode;
}
//循环结束后者即为倒数第k个节点的前一个节点,改变指向即可删除
latter.nextNode = latter.nextNode.nextNode;
return true;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
@Test
public void test_DeeteCountdownKNode(){
ListNode head = new ListNode("lemon");
head.nextNode = new ListNode("lll");
head.nextNode.nextNode = new ListNode(1);
head.nextNode.nextNode.nextNode = new ListNode('a');
head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
DeleteCountdownKNode deleteCountdownKNode = new DeleteCountdownKNode();
Boolean bol = deleteCountdownKNode.deleteCountdownNode(head,2);
System.out.println(bol);
while(head!=null){
System.out.print(head.data+"->");
head= head.nextNode;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度&空间复杂度
1.带环?:我们设置快慢节点pre和lat,每次使得快节点走两步,
慢节点走一步,当两个节点相遇时就表明存在环。
2.求环的长度:以是否带环问题,我们可以得到相遇点,
由此,我们可以进行遍历计数得到环的长度。
3.求环的入口点:设置快慢节点pre和lat,使得快节点先走环
长度length步,紧接着一起走,当两个节点相等时则表明为入口点。
- 1
- 2
- 3
- 4
- 5
- 6
#
package com.struct.interview_question.list_interview_question.cyclelist;
import com.struct.interview_question.list_interview_question.ListNode;
/**
*@Description: 判断链表是否带环
*@Author: dyy
*/
public class ListHasCycle {
public boolean hasCycle(ListNode head){
if(head==null||head.nextNode==null){
return false;
}
ListNode pre = head;
ListNode lat = head;
while (pre!=null){
pre = pre.nextNode.nextNode;
lat = lat.nextNode;
//当两个节点相遇时就表明有环
if(pre == lat){
return true;
}
}
return false;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
#
package com.struct.interview_question.list_interview_question.cyclelist;
import com.struct.interview_question.list_interview_question.ListNode;
/**
*@Description: 求带环链表环的长度
*@Author: dyy
*/
public class CycleLength {
//判断链表是否带环,返回相交点
public ListNode hasCycle(ListNode head){
if(head==null||head.nextNode==null){
return null;
}
ListNode pre = head;
ListNode lat = head;
while (pre!=null){
pre = pre.nextNode.nextNode;
lat = lat.nextNode;
if(pre==lat){
return pre;
}
}
return null;
}
//求环的长度,参数为相交点
public int cycleLength(ListNode node){
ListNode current = node;
int length = 0;
while (current!=null){
current = current.nextNode;
length++;
if(current==node){
return length;
}
}
return length;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
package com.struct.interview_question.list_interview_question.cyclelist;
import com.struct.interview_question.list_interview_question.ListNode;
/**
*@Description: 求带环链表的环的入口点
*@Author: dyy
*/
public class CycleListEntryPoint {
public ListNode findEntryPoint(ListNode head,int cycleLength){
if(head == null){
return null;
}
ListNode pre = head;
ListNode lat = head;
for(int i = 0; i < cycleLength;i++){
pre = pre.nextNode;
}
while (pre!=null && lat!=null){
pre = pre.nextNode;
lat = lat.nextNode;
if(pre==lat){
return pre;
}
}
return null;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
//测试代码
/**
*@Description: 链表是否带环测试代码啊
*@Author: dyy
*/
@Test
public void test_ListHasCycle(){
ListNode head = new ListNode("lemon");
head.nextNode = new ListNode("lll");
head.nextNode.nextNode = new ListNode(1);
head.nextNode.nextNode.nextNode = new ListNode('a');
head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
ListNode tail = head.nextNode.nextNode.nextNode.nextNode;
tail.nextNode = head.nextNode.nextNode;
ListHasCycle listHasCycle = new ListHasCycle();
boolean bol = listHasCycle.hasCycle(head);
System.out.println(bol);
}
/**
*@Description: 链表带环长度测试代码
*@Author: dyy
*/
@Test
public void test_CycleListLength(){
ListNode head = new ListNode("lemon");
head.nextNode = new ListNode("lll");
head.nextNode.nextNode = new ListNode(1);
head.nextNode.nextNode.nextNode = new ListNode('a');
head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
ListNode tail = head.nextNode.nextNode.nextNode.nextNode;
tail.nextNode = head.nextNode.nextNode;
CycleLength cycleLength = new CycleLength();
//得到相交点
ListNode node = cycleLength.hasCycle(head);
int count = cycleLength.cycleLength(node);
System.out.println(count);
}
/**
*@Description: 链表带环求环的入口点测试代码
*@Author: dyy
*/
@Test
public void test_CycleLisEntryPoint(){
ListNode head = new ListNode("lemon");
head.nextNode = new ListNode("lll");
head.nextNode.nextNode = new ListNode(1);
head.nextNode.nextNode.nextNode = new ListNode('a');
head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
ListNode tail = head.nextNode.nextNode.nextNode.nextNode;
tail.nextNode = head.nextNode.nextNode;
CycleLength cycleLength = new CycleLength();
//得到相交点
ListNode node = cycleLength.hasCycle(head);
int count = cycleLength.cycleLength(node);
CycleListEntryPoint cycleListEntryPoint = new CycleListEntryPoint();
ListNode node1 = cycleListEntryPoint.findEntryPoint(head,count);
System.out.println(node1.data);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59