删除重复链表
题目描述: 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
解题思路:
(1)新建一个指向头节点pHead的新节点Head
(2)tmp指向链表的实际头节点pHead
(3)同时需要一个pre节点来保存当前节点的前一节点的,初始化时,pre=Head;以便于删除重复节点后能连接后续的节点
分两种情况讨论:当头节点就有相同的节点的时候,此时,Head.next=tmp.next;
当相同的节点不在头节点的时候,pre=pre.next;(指向当前链表的头节点),tmp=tmp.next;
图解算法
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead){
if(pHead==null || pHead.next==null){
return pHead;
}
ListNode Head=new ListNode(0);
Head.next=pHead;
ListNode pre=Head;
ListNode tmp=Head.next;
while(tmp!=null){
if(tmp.next!=null && tmp.val==tmp.next.val){
while(tmp.next!=null && tmp.val==tmp.next.val){
tmp=tmp.next;
}
pre.next=tmp.next;
tmp=tmp.next;
}else{//头节点不相同的情况
pre=pre.next;
tmp=tmp.next;
}
}
return Head.next;
}
}
链表中找环的入口
题目描述:
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
解题思路:
(1)使用快慢指针,fast=fast.next.next;slow=slow.next;快指针走过的路程为慢指针的两倍。如果有环,两者必然会相遇。
(2)第一次相遇后,将slow复原到pHead,fast仍然从相遇点出发,当两者以相同的速读再次前进时,下一次相遇点则为环的起点。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead==null || pHead.next==null){
return null;
}
ListNode slow=pHead;
ListNode fast=pHead;
while(fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(slow==fast){
break;
}
}
//第一次相遇,此时让slow回到起点,slow和fast以相同的速度前行,
//他们的再次相遇点位环的起点
slow=pHead;
while(slow!=fast){
fast=fast.next;
slow=slow.next;
}
//相遇,slow为环状的起点
return slow;
}
}
约瑟夫环问题
问题描述:
首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
解题步骤:
(1)用java中的list集合来模拟约瑟夫环问题,模拟环状的关键在于:index%list.size();
(2)初始化index为要删除的节点前一个位置,index+m则表示为当前要删除的节点位置,删除完成节点后,需要将index–;即移动到前一个位置,便于下次循环继续删除。
import java.util.*;
public class Solution {
public int LastRemaining_Solution(int n, int m) {
//特判
if(n==0 || m==0){
return -1;
}
LinkedList<Integer> list=new LinkedList<Integer>();
//创建循环链表
for(int i=0;i<n;i++){
list.add(i);
}
//表示index在头节点之前的位置
int index=-1;
while(list.size()>1){
index=(index+m)%(list.size());
list.remove(index);
index--;//将index回到头节点之前的位置,即上一个节点
}
return list.get(0);
}
}
两条链表中的公共节点
题目描述:
输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
解题思路分析
(1)使用双指针方法,同时遍历两条链表。
(2)如果存在公共节点,那么,两个指针必然会相交。
(3)但是两条链表的长短不一,这种情况下,必然有一个指针会先停下来(如果没有找到公共节点),这个时候,就让这个指针指向另外一条链表的头节点,但另外一个指针也结束后,也让他指向另外一条链表。如此反复进行遍历,如果有节点,他们必将指向同一个节点。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1==null && pHead2==null){
return null;
}
if(pHead1==null || pHead2==null){
return null;
}
ListNode p=pHead1;
ListNode q=pHead2;
while(p!=q){
p=p.next;
q=q.next;
if(p!=q && p==null){
p=pHead2;
}
if(p!=q && q==null){
q=pHead1;
}
}
return p;
}
}
复制复杂的链表问题
问题描述:
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
解题思路:
由于存在Random指针,可以考虑用map将原链表中的每个节点的值保存起来。然后依次按照链表的的节点顺序从map中取出复制的节点,用于构建新的链表。分别用p、q指针指向原链表和目标链表。
直接上代码更加清晰动人
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
import java.util.*;
public class Solution {
public RandomListNode Clone(RandomListNode pHead){
if(pHead==null){
return null;
}
RandomListNode p = pHead;
//新建节点
RandomListNode target = new RandomListNode(p.label);
RandomListNode q=target;
HashMap<RandomListNode,RandomListNode> map=new HashMap<RandomListNode,RandomListNode>();
//创建两条链表之间的映射关系
while(pHead!=null){
map.put(pHead,new RandomListNode(pHead.label));
pHead=pHead.next;
}
//开始复制
while(q!=null){
q.next=map.get(p.next);//p点的下一个节点
q.random=map.get(p.random);//p的random节点
p=p.next;
q=q.next;
}
return target;
}
}
合并两条链表
题目描述:
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路分析:
(1)设置两个指针p和q分别指向两条链表,新建一条target链表用作最后的结果返回,同时给target新建一个辅助指针t:t=target
(2)比较p和q节点的值,分情况讨论,存在三种情况:
- p.val< q.val,则t的下个节点接p
- p.val==q.val,则t需要分别接p和q
- p.val>q.val,则他需要接q;
(3)最后,可能存在两条链表不一样长的情况,同时需要考虑两种情况,把长的剩余的链表直接到t的后面
(4)最后注意是返回target.next
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null && list2==null){
return null;
}
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
ListNode p=list1;
ListNode q=list2;
ListNode target=new ListNode(-1);
ListNode t=target;
while(p!=null && q!=null){
if(p.val<q.val){
t.next=p;
p=p.next;
t=t.next;
}else if(p.val==q.val){
t.next=p;
p=p.next;
t=t.next;
t.next=q;
q=q.next;
t=t.next;
}else{
t.next=q;
q=q.next;
t=t.next;
}
}
//至少有一条链表先完成
if(p==null){
t.next=q;
t=t.next;
}else if(q==null){
t.next=p;
t=t.next;
}else{
return target.next;
}
return target.next;
}
}
链表的反转
题目描述:
给定一个链表,求出该链表的反转链表
解题思路1:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head==null){
return null;
}
ListNode pre=null;
ListNode cur=head;
ListNode next=null;
while(cur!=null){
next=cur.next;
//连接逆转链表
cur.next=pre;
//三个指针向后移动
pre=cur;
cur=next;
}
return pre;
}
}
解题思路2:
这种方式需要耗费额外的内存(需要复制每一个节点)
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head==null){
return null;
}
ListNode p=head;
ListNode target=new ListNode(-1);
ListNode t=target;
while(p!=null){
ListNode q=new ListNode(p.val);//需要复制节点
q.next=t.next;
t.next=q;
// 拼接完成
p=p.next;
}
return target.next;
}
}
输出链表倒数第K个节点
题目描述:
输入一个链表,输出该链表中倒数第k个结点。
解题思路:
(1)注意特判情况,当链表的长度小于输入参数K时,无效
(2)设置p和q两个指针,让p先走k步,然后让p和q同步走,当q为null时,此时p所指向的节点即为倒数第k个节点
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(count(head)<k){
return null;
}
ListNode p=head;
ListNode q=head;
for(int i=0;i<k;i++){
q=q.next;
}
while(q!=null){
q=q.next;
p=p.next;
}
return p;
}
private int count(ListNode head){
if(head==null){
return 0;
}
ListNode tmp=head;
int count=0;
while(tmp!=null){
count++;
tmp=tmp.next;
}
return count;
}
}
从尾到头打印链表
解题思路:
(1)先反转链表,然后再遍历即可
(2)使用栈先存储链表,然后从栈中取出数据并打印;
这里以第(2)种方案为案例进行代码演示:
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.*;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> res=new ArrayList<Integer>();
if(listNode==null){
return res;
}
Stack<Integer> stack=new Stack<Integer>();
ListNode tmp=listNode;
while(tmp!=null){
stack.add(tmp.val);
tmp=tmp.next;
}
while(!stack.isEmpty()){
res.add(stack.pop());
}
return res;
}
}