单链表的定义
public class NodeList {
int val;
NodeList next;
//初始化一个单向链表
public NodeList(int val){
this.val=val;
next=null;
}
}
1.两个链表的第一个公共字节点
方法一思路:链表A存入Hashset集合,并遍历Hashset集合查看B中是否有节点与之匹配,若有则是链表AB的第一个公共字节点,若无则AB链表无公共字节点
public NodeList findCommomNode(NodeList headA,NodeList headB){
//定义一个hashset:无序且不重复;用来存放A链表
HashSet<NodeList> set = new HashSet<>();
while (headA!=null){
set.add(headA);
headA=headA.next;
}
while (headB!=null){
if (set.contains(headB))
return headB;
headB = headB.next;
}
return null;
}
方法二思路:栈(先进后出)存放链表,并遍历查找直到链表的栈顶元素不同,则共同节点则是此时栈顶元素的前一个
//栈的做法
public NodeList findCommomNodeByTrack(NodeList headA,NodeList headB){
//创建两个栈对象,用于存放链表A和链表B
Stack<NodeList> stackA = new Stack<>();
Stack<NodeList> stackB = new Stack<>();
while (headA!=null){
stackA.push(headA);
headA=headA.next;
}
while (headB!=null){
stackA.push(headB);
headB=headB.next;
}
NodeList preNode = null;
while (stackA.size()>0 && stackB.size() > 0){
//查看栈顶元素是否相同
if (stackA.peek()==stackB.peek()){
preNode=stackA.pop();
stackB.pop();
}else {
break;
}
}
return preNode;
}
2.判断链表是否位回文链表
1-2-2-1 为回文链表,1-2-3-1不是
思路:将要判断的链表head放入栈中stackNodeList,一边stackNodeList出栈一边遍历head链表,并比较,若有一个不相等,则不是回文链表
public boolean isReturnNodeList(NodeList head){
NodeList temp = head;
Stack<Integer> stackNodeList = new Stack<>();
//把链表节点的值放至栈中
while (temp!=null){
stackNodeList.push(temp.val);
temp=temp.next;
}
while (head!=null){
if (head.val != stackNodeList.pop()){
return false;
}
head=head.next;
}
return true;
}
3.1合并两个有序单链表
思路:遍历两个链表的节点值,一次对比取出较小的节点接入新链表......
public NodeList mergeOrderNodeLists(NodeList headA,NodeList headB){
//创建一个虚拟头节点,用于指向合并后的新链表
NodeList nodeListHead = new NodeList(0);
NodeList head =nodeListHead;
//遍历A,B链表
while (headA!=null && headB!=null){
//判断AB链表节点的值
if (headA.val<=headB.val){
head.next=headA;
headA=headA.next;
}else {
head.next=headB;
headB=headB.next;
}
return head;
}
//若有一方链表为空,直接把该链表接入nodeListHead后面
head.next = headA == null ? headB : headA ;
return head;
}
3.2合并K个有序链表
思路:在合并两个有序链表的情况下,采用递归的方法
public NodeList mergeManyNodeList(NodeList[] manyNodeLists){
//定义合并后的新链表
NodeList newNodeList = null;
//遍历链表集合,并递归合并两个有序链表
for (NodeList nodeList:manyNodeLists){
newNodeList = mergeOrderNodeLists(newNodeList, nodeList);
}
return newNodeList;
}
3.3链表A区间删除,并在删除区间拼接链表B
题意:给定一个链表A,链表B,将链表A的下标I至J位置的节点删除,并拼接上链表
思路:遍历要删除的链表A,找到要删除节点I的前一个节点preNode,要删除节点J的后一个节点afterNode ;遍历B链表,找到B链表的尾节点 endB
preNode.next = B;
//B链表的尾节点 与 A链表删除后的后半部分 连接
endB.next = afterNode;
4.1.双指针
给定一个链表,返回中间节点,若改链表是偶数链节点,返回中间节点的第二个
例如 1-2-3-4 返回 3
public NodeList findMiddleNodeList(NodeList head){
//定义两个指针,快慢指针
NodeList slow= head;
NodeList fast =head;
//当快指针达到链表末尾,慢指针一定处于链表中间位置
// while条件中跳出循环:快指针的下一个节点为空,此时偶数节点链表会返回中间节点的第二个
// 如果条件是 fast.next.next != null,偶数节点链表会返回中间节点的第一个
while (fast!=null && fast.next !=null){
slow=slow.next;
fast=fast.next.next;
}
return slow;
}
4.2返回链表倒数第K个节点
思路:定义快慢指针,刚开始让快指针与满指针间隔K个节点,当快指针走到链表尾部空节点,慢指针刚到走到倒数第K个节点
public NodeList countdown_K_NodeList(NodeList head,int key){
//定义快慢节点
NodeList fast = head;
NodeList slow = head;
//把快节点先前进k+1
while (fast!=null && key>0){
fast=fast.next;
key--;
}
//判断fast节点是否到达链表尾部空节点处
while (fast!=null){
slow=slow.next;
fast=fast.next;
}
return slow;
}
4.3旋转链表
5.1删除链表特定节点
思路:定义一个虚拟节点(0),此时不用单独考虑首节点删除的情况
遍历链表,当链表的当前节点的next节点的val值=要删除的val,把当前节点的next值指向当前节点的next节点的next节点
public NodeList deleteByVal(NodeList head,int val){
//创建一个虚拟节点,它的next指向head,此时不用单独考虑删除首节点情况
NodeList virtuallyHead =new NodeList(0);
virtuallyHead.next = head ;
//当前节点curNode
NodeList curNode = virtuallyHead;
//遍历链表
while (curNode.next!=null){
if (curNode.next.val == val){
curNode.next=curNode.next.next;
curNode=curNode.next;
}else {
curNode=curNode.next;
}
}
return virtuallyHead;
}
5.2删除倒数第n个节点
方法一:遍历链表,找到要删除节点的前一个节点,并将要删除节点的前一个节点 的 next 指向
要删除节点的前一个节点 的 next.next节点...
public NodeList removeNthFromEnd(NodeList head,int k){
//定义虚拟节点
NodeList dummy = new NodeList(0);
dummy.next = head ;
//获取链表的长度 调用getLengthNodeList():自编获取单链表长度的方法
int length = getLengthNodeList(head);
//找到要删除节点的前一个节点 curNode
NodeList curNode = dummy;
for (int i = 1 ;i<length - k +1 ;++i){
curNode=curNode.next;
}
curNode.next = curNode.next.next;
NodeList result = dummy.next;
return result;
}
//获取链表长度
public int getLengthNodeList(NodeList head){
int length = 0 ;
while (head!=null){
length=length+1;
head=head.next;
}
return length;
}
方法二:双指针法
思路:first指针先前进k步,然后first指针和second指针同时前进,当first指针的下一个指针为空,
second指针的下一个节点即是要删除的节点
public NodeList removeNthFromEnd(NodeList head,int k){
//定义虚拟节点
NodeList dummy = new NodeList(0);
dummy.next=head;
NodeList first = dummy.next;
NodeList second =dummy;
//同步走前,first比second提前走k
for (int i =0;i<k;++i){
first=first.next;
}
//first,second同步走
while (first!=null){
first=first.next;
second=second.next;
}
//跳出循环,此处second是删除节点的前一个节点
second.next =second.next.next;
NodeList result = dummy.next;
return result;
}
5.3删除重复元素(给定链表有序的)
5.3.1重复元素保留一个
遍历链表,如果当前节点 与 当前节点的下一个 节点 相等,把当前节点的next指针 指向 当前节点的next的next节点(即删除了当前节点的下一个节点)
public NodeList deleteEqualNodeList(NodeList head) {
if (head == null) {
return head;
}
NodeList curNode = head;
//循环遍历链表
while (curNode.next != null) {
if (curNode.val == curNode.next.val) {
curNode.next = curNode.next.next;
} else {
curNode = curNode.next;
}
}
return head;
}