查找单链表的中间节点,要求只能遍历一次链表
在只能遍历一遍链表的基础上我们需要查找到中间节点,则需
设置一个快慢节点fast&slow,当我们每次只让慢节点走一步
快节点走两步,则当快节点为空时,则慢节点即为要查找的中间节点。
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;
while(fastNode!=null && fastNode.nextNode!=null){
slowNode = slowNode.nextNode;
fastNode = fastNode.nextNode.nextNode;
}
return slowNode;
}
}
@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)
}
查找单链表的倒数第k个节点
方案一:我们按照正常的思路,遍历链表,记录链表的总长度
当我们知道链表的总长度count后,直接输出链表的count-k个元素即可
package com.struct.interview_question.list_interview_question.findmiddlenode;
import com.struct.interview_question.list_interview_question.ListNode;
public class FindCountdownKNode {
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;
}
}
方案二:在之前只遍历一遍找到中间节点的基础上,我们可以
令一个走的快的节点fast和一个走的慢的节点slow,使ast先走
k-1步,若fast为空则表明k大于链表的总长度,输出null,
紧接着,我们可以使fast和slow同时走,当fast.next为空时,slow即为所求的倒数第k个节点
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;
if(fast==null){
return null;
}
}
while (fast.nextNode!=null){
slow = slow.nextNode;
fast = fast.nextNode;
}
return slow;
}
}
@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)
}
删除链表的倒数第K个结点
(1)当链表的长度刚好等于n时,则要删除的节点刚好是头结点,
(2)链表的长度大于n时,我们使前者节点premer和后者节点latter
同时向后移动,知道premer.next为null时,则latter节点刚还为
要删除节点的前一个节点,我们可以通过改变指向来进行删除。
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 {
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;
}
latter.nextNode = latter.nextNode.nextNode;
return true;
}
}
@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.带环?:我们设置快慢节点pre和lat,每次使得快节点走两步,
慢节点走一步,当两个节点相遇时就表明存在环。
2.求环的长度:以是否带环问题,我们可以得到相遇点,
由此,我们可以进行遍历计数得到环的长度。
3.求环的入口点:设置快慢节点pre和lat,使得快节点先走环
长度length步,紧接着一起走,当两个节点相等时则表明为入口点。
#
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;
}
}
#
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;
}
}
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;
}
}
/**
*@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);
}