链表相关题目汇总

目录

前言

1.反转单链表

2.反转部分单链表

3.查找单链表中的倒数第K个结点(k > 0)

 4.查找单链表的中间结点

 5.从尾到头打印单链表

6.已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序

 7.判断一个单链表是否有环,如果存在环,求第一个入环节点

8.判断两个单链表是否相交(分有环和无环),若相交,求交点

9.删除排序链表中的重复节点(有重复的全删除)

10.删除排序链表中的重复节点(有重复的删除重复,只保留一个)

 11.旋转链表

12.将单链表的每K个节点之间逆序 


前言

本博文收集一些从《剑指Offer》、《程序员代码面试指南》以及LeetCode部分刷题中总结出来的与链表相关的题目,收集的都是比较简单的,欢迎补充,如果有写错的欢迎批评。持续更新中。

1.反转单链表

public ListNode reverse(ListNode head){
	if(head==null||head.next==null){
		return head;
	}
	ListNode pre=null;
	ListNode next=null;
	while(head!=null){
		next=head.next;
		head.next=pre;
		pre=head;
		head=next;
	}
	return pre;
}

2.反转部分单链表

举个例子吧,假设要反转的链表范围为2~4,将1-2-3-4-5反转为1-4-3-2-5,那么如何做呢?

找到第二个位置的前面一个节点fPre以及第四个位置的后面一个节点tPos,把反转的部分先反转,然后连接fPre和tPos即可。

如果fPre节点为空的话,说明反转部分是包含头节点的,则返回新的头节点

public ListNode reversePartListNode(ListNode head,int from,int to) {
		if(head==null||head.next==null) {
			return head;
		}
		
		ListNode fPre=null;
		ListNode tPos=null;
		ListNode cur=head;
		int len=0;
		while(cur!=null) {
			len++;
            //分别找到fPre和tPos
			fPre=len==from-1?cur:fPre;
			tPos=len==to+1?cur:tPos;
		}
        //判断包不包含头节点,如果不包含让cur指向cur的下一个节点
		cur=fPre==null?head:fPre.next;
		ListNode node2=cur.next;
		cur.next=tPos;
		ListNode next=null;
		while(node2!=tPos) {
			next=node2.next;
			node2.next=cur;
			cur=node2;
			node2=next;
		}
		if(fPre!=null) {
			fPre.next=cur;
			return head;
		}
		return cur;
	}

3.查找单链表中的倒数第K个结点(k > 0)

 定义两个指针,其中一个指针先走k步,然后两个一起走,当第一个指针到达终点时,第二个指针来到倒数第k个节点的位置

删除倒数第k个节点的题也类似,这里不说了

public ListNode kthListNode(ListNode head,int k){
	if(head==null){
		return head;
	}
	ListNode cur1=head;
	ListNode cur2=head;
	while(k>0){
		cur1=cur1.next;
	}
	while(cur1!=null&&cur2!=null){
		cur1=cur1.next;
		cur2=cur2.next;
	}
	return cur2;
}

 4.查找单链表的中间结点

定义两个指针,一个快指针,一个慢指针,快一次走两步,慢一次走一步,当快指针的next.next为空时,慢指针的位置就是中点位置。

public ListNode MidListNode(ListNode head){
	if(head==null||head.next==null){
		return head;
	}
	ListNode cur1=head;
	ListNode cur2=head;
	while(cur1.next.next!=null&&cur2.next!=null){
		cur1=cur1.next.next;
		cur2=cur2.next;
	}
	return cur2;	
}

 5.从尾到头打印单链表

利用栈

public void printFromEndToStart(ListNode head){
	if(head==null){
		return;
	}
	if(head.next==null){
		System.out.print(head.val);
	}
	Stack<ListNode> stack=new Stack<ListNode>();
	while(head!=null){
		stack.push(head);
		head=head.next;
	}
	while(!stack.isEmpty()){
		System.out.print(stack.pop()+",");
	}	
}

 

6.已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序

比较简单也不多说

public ListNode MergeListNode(ListNode head1,ListNode head2){
	if(head1==null&&head2!=null){
		return head2;
	}
	if(head2==null&&head1!=null){
		return head1;
	}
	ListNode newHead=new ListNode(-1);
	ListNode cur=newHead;
	while(head1!=null&&head2!=null){		
		if(head1.val>head2.val){
			cur.next=head2;
			cur=cur.next;
			head2=head2.next;
		}else{
			cur.next=head1;
			cur=cur.next;
			head1=head1.next;			
		}
	}
	while(head1!=null){
		cur.next=head1;
		head1=head1.next;
		
	}
	while(head2!=null){
		cur.next=head2;
		head2=head2.next;		
	}
	return newHead.next;
}

 7.判断一个单链表是否有环,如果存在环,求第一个入环节点

//判断是否有环,并寻找入环节点
public ListNode isCircle(ListNode head){
//定义两个指针,一个快指针一次走两步,一个慢指针一次走一步,若有环,则必会相遇
	ListNode cur1=head;
	listNode cur2=head;
	while(cur1!=cur2){
		if(cur1==null||cur2==null){
			return null;
		}
		cur1=cur1.next.next;
		cur2=cur2.next;
	}	
	//寻找入环节点,两个指针再一起走,相遇时就是入环节点
	cur1=head;
	while(cur1!=cur2){
		cur1=cur1.next;
		cur2=cur2.next;
	}
	return cur1;
}

8.判断两个单链表是否相交(分有环和无环),若相交,求交点

1.针对两个无环的链表的情况

(1)分别求出两个链表的长度len1和len2

(2)让长度长的先走(len1-len2)步,然后一起走,走到第一次相同的点就是交点

2.针对两个有环的链表的情况

(1)由于两个链表都有环,我们可以分别求出两个链表的入环节点loop1和loop2

(2)如果loop1和loop2相等,求出两个链表从头节点到loop节点的的长度,同样长的先走,然后一起走,直到第一次相遇,

(3)如果loop1和loop2不相等,如果从loop1出发在回到loop1之前遇到了loop2,则说明两个链表是相交的,否则两个链表就是不相交的。

9.删除排序链表中的重复节点(有重复的全删除)

例如:1-2-3-3-4-4-5,删除后就是1-2-5

public class DeleteRepListNode {
	public ListNode remove(ListNode head) {
		if(head==null) {
			return head;
		}
		ListNode cur=head;
		ListNode pre=null;
		boolean flag=false;
		while(cur!=null) {
			//表示出现重复节点,找出所有的重复的节点
			while(cur.next!=null&&cur.val==cur.next.val) {
				flag=true;
				cur=cur.next;
			}
			if(!flag) {
				pre=cur;
				cur=cur.next;
			}
			else{ //处理重复的节点
				flag=false;
				if(pre==null) {  //表示从头节点开始就重复
					head=cur.next;
					cur=head;
				}else {
					pre.next=cur.next;
					cur=pre.next;
				}
			}
						
		}
		return head;
	}

}

10.删除排序链表中的重复节点(有重复的删除重复,只保留一个)

例如:1-2-3-3-4-4-5,删除后就是1-2-3-4-5

思路:两个指针分别指向链表的第一个结点及第二个结点,即 pre 指向第一个结点、cur 指向第二个结点;若 pre.value == cur.value,则 pre 的 next 指向 cur 的 next ,cur 指向 cur 的 next ;若不等,则 pre 指向 cur,cur 指向 cur 的 next;直到循环结束

public static void deleteRepeteNode(Node head) {
        Node pre = head.next;
        Node cur;
        while (pre != null) {
            cur = pre.next;
            if (cur != null && (pre.value == cur.value)) {
                pre.next = cur.next;
            } else {
                pre = cur;
            }
        }
    }

 11.旋转链表

https://leetcode-cn.com/problems/rotate-list/description/

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

1.首先分析这个k的取值,求出链表的长度len,向右移动的步数m=k%len

2.向右移动怎么做呢?详细步骤在注释里面

public ListNode rotateRight(ListNode head, int k) {
        if(head==null){
            return head;
        }
        //1.求出链表长度len
            int len=1;
            ListNode cur=head;
            while(cur.next!=null){
                len++;
                cur=cur.next;
            }
            //2.定义要向右移动的步数m
            int m=k%len;
            if(m==0){
                return head;
            }

            //3.指针从头节点开始向右移动len-m-1,找到要移动的节点的前一个节点pre
            ListNode pre=head;
            int step=len-m-1;
            while(step>0){
                pre=pre.next;
                step--;
            }
            ListNode next=pre.next;
            //4.保存要移动的节点区间的最后一个节点cur,将pre的next指向cur的next
            pre.next=cur.next;
            cur.next=head;
            return next;
    }

12.将单链表的每K个节点之间逆序 

package 链表问题;

import java.util.Stack;

/*
 * 将单链表的每K个节点之间逆序
 */
public class ReverseKNodes {
	
	
	public static ListNode reverseKNode(ListNode head,int k) {
		if(k<2) {
			return head;
		}
		Stack<ListNode> stack=new Stack<>();
		ListNode newHead=head;
		ListNode cur=head;
		ListNode pre=null;
		ListNode next=null;
		while(cur!=null) {
			next=cur.next;
			stack.push(cur);
			if(stack.size()==k) {
				//求出k个节点逆序之后的最后一个节点
				pre=reverse(stack,pre,next);
				newHead=newHead==head?cur:newHead;
			}
			cur=next;
		}
		return newHead;		
	}
	private static ListNode reverse(Stack<ListNode> stack, ListNode pre, ListNode next) {
		// TODO Auto-generated method stub
		ListNode cur=stack.pop();
		if(pre!=null) {
			pre.next=cur;
		}		
		while(!stack.isEmpty()) {
			ListNode temp=stack.pop();
			cur.next=temp;
			cur=temp;
		}
		cur.next=next;
		return cur;
	}
	
	public static void main(String[] args) {
		ListNode node1=new ListNode(1);
		node1.next=new ListNode(2);
		node1.next.next=new ListNode(3);
		node1.next.next.next=new ListNode(4);
		node1.next.next.next.next=new ListNode(5);
		node1.next.next.next.next.next=new ListNode(6);
		node1.next.next.next.next.next.next=new ListNode(7);
		node1.next.next.next.next.next.next.next=new ListNode(8);
	
		ListNode res=reverseKNode(node1,3);
		ListNode cur=res;
		while(cur!=null) {
			System.out.print(cur.val+" ");
			cur=cur.next;
		}
	}

}

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值