查找单链表的中间节点,要求只能遍历一次链表
在只能遍历一遍链表的基础上我们需要查找到中间节点,则需
设置一个快慢节点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);
}