文章目录
0、节点定义
//单链表
class Node {
public int value;
public Node next;
public Node(int value) {
this.value=value;
}
}
//双链表
class DoubleNode{
public int value;
public DoubleNode last;
public DoubleNode next;
public DoubleNode(int data) {
this.value=data;
}
}
一、打印两个有序链表的公共部分
给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。
//打印两个有序链表的公共部分
public void printCommonPart(Node head1, Node head2)
{
System.out.println("Common Part:");
if(head1==null || head2==null) {
return ;
}
while(head1!=null && head2!=null) {
if (head1.value==head2.value) {
System.out.print(head2.value + " ");
head1=head1.next;
head2=head2.next;
}else if (head1.value>head2.value){
head2=head2.next;
}else {
head1=head1.next;
}
}
System.out.println();
}
二、在单链表和双链表中删除倒数第K个节点
如果链表的长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)。
//单链表中删除倒数第K个节点
public Node deletelastKthNode(Node head,int lastKth) {
// 若不需要删除,则返回头结点
if (head==null || lastKth<1) {
return head;
}
Node cur=head;
while(cur!=null) {
lastKth--;
}
if (lastKth==0) {
return head.next;
}
if (lastKth==0) {
return head.next;
}
if (lastKth<0) {
cur=head;
while(++lastKth!=0) {
cur=cur.next;
}
cur=cur.next;
}
return head;
}
//双链表中删除倒数第K个节点
public DoubleNode deleteLastkthNode(DoubleNode head, int lastKth) {
if(head==null || lastKth<1) {
return head;
}
DoubleNode cur=head;
while(cur!=null) {
cur=cur.next;
lastKth--;
}
if (lastKth==0) {
head = head.next;
head.last=null;
}
if (lastKth<0) {
cur=head;
while(++lastKth!=0) {
cur=cur.next;
}
DoubleNode next = cur.next.next;
cur.next=next;
//next.last=cur;
if(next!=null) {
next.last=cur;
}
}
return head;
}
三、删除链表的中间节点
给定链表的头结点head,实现删除链表的中间节点的函数。
如:
不删除任何节点;
1->2,删除节点1;
1->2->3,删除节点2
1->2->3->4,删除节点2;
1->2->3->4->5,删除节点3;
// 删单链表的中间节点
public Node removeMidNode(Node head) {
//无节点和只有一个节点时,不删
if (head==null || head.next==null) {
return head;
}
// 2个节点
if(head.next!=null && head.next.next==null) {
return head.next;
}
Node pre=head;
Node cur=head.next.next;
while(cur.next!=null && cur.next.next!=null) {
pre=pre.next;
cur=cur.next.next;
}
pre.next=pre.next.next;
return head;
}
四、反转单向和双向链表
最好能做到代码一次成型,运行不出错。
// 反转单链表
public Node reverseNode(Node head) {
if (head==null) {
return head;
}
Node pre=null;
Node next=null;
while(head!=null) {
next=head.next;
head.next=pre;
pre=head;
head=next;
}
return pre;
}
// 反转双链表
public DoubleNode reverseDoubleNode(DoubleNode head) {
if (head==null) {
return head;
}
DoubleNode pre=null;
DoubleNode next=null;
while(head!=null) {
next=head.next;
head.next=pre;
head.last=next;
pre=head;
head=next;
}
return pre;
}
五、反转部分单向链表
给定一个单向链表的头节点head,以及两个整数from和to,在单向链表上把第from个节点到第to个节点这一部分进行反转。
例如:
1->2->3->4->5->null,from=2,to=4
调整结果为:1->4->3->2->5->null
再如:
1->2->3->null, from=1, to=3
调整结果为:3->2->1->null
// 部分反转单链表
public Node reversePart(Node head,int from,int to) {
if (head==null) {
return head;
}
Node node1=head;
int len=0;
Node fPre=null;
Node toPos=null;
while(node1!=null) {
len++;
fPre=(len==from-1?node1:fPre);
toPos=(len==to+1?node1:toPos);
node1=node1.next;
}
if(from>=to || from<1 || to>len) {
return head;
}
node1 =(fPre==null?head:fPre.next);
Node node2=node1.next;
node1.next=toPos;
Node next=null;
while(node2!=toPos) {
next=node2.next;
node2.next=node1;
node1=node2;
node2=next;
}
if(fPre!=null) {
fPre.next=node1;
return head;
}
return node1;
}
六、判断一个链表是否为回文结构
给定一个链表的头节点head,请判断该链表是否为回文结构。
例如:
1->2->1->null,返回true;
1->2->2->1->null,返回true;
1->2->3->null,返回false;
// 利用栈结构,判断链表是否为回文结构
public boolean isPalindromel(Node head) {
Stack<Node> stack = new Stack<Node>();
Node cur =head;
while(cur!=null) {
stack.push(cur);
cur=cur.next;
}
cur=head;
while(cur!=null) {
if(stack.pop().value != cur.value) {
return false;
}
cur=cur.next;
}
return true;
}
七、将单向链表按某值划分成左边小、中间相等、右边大的形式
给定一个单向链表的头节点head,节点的值类型是整型,再给定一个整数pivot。实现一个调整链表的函数,将链表调整为左部分都是小于pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于pivot的节点。除了这个要求外,对调整后的节点顺序没有更多的要求。
例如:链表9->0->4->5->1,pivot=3.
调整后链表可以是1->0->4->9->5,也可以是:0->1->9->5->4。
//单向链表按某值划分
public Node listPartition1(Node head, int pivot) {
if (head==null) {
return head;
}
int k=0;
Node cur=head;
while(cur!=null) {
k++;
cur=cur.next;
}
Node[] nodeArr = new Node[k];
cur=head;
k=0;
while(cur!=null) {
nodeArr[k++]=cur;
cur=cur.next;
}
// 数组内元素调整
arrPartition(nodeArr, pivot);
// 数组拼接成链表
for(k=1; k < nodeArr.length; k++) {
nodeArr[k-1].next=nodeArr[k];
}
nodeArr[k-1].next = null;
return nodeArr[0];
}
public void swapNode(Node[] nodeArr, int a,int b) {
Node temp = nodeArr[a];
nodeArr[a]=nodeArr[b];
nodeArr[b]=temp;
}
public void arrPartition(Node[] nodeArr, int pivot) {
int small=-1;
int len=nodeArr.length;
int index=0;
while(index!=len) {
if(nodeArr[index].value<pivot) {
swapNode(nodeArr, ++small, index++);
}else if (nodeArr[index].value == pivot) {
index++;
}else {
swapNode(nodeArr, --len, index);
}
}
}
八、两个单链表生成相加链表
假设链表中每个节点的值都在0~9之间,那么链表整体就可以代表一个整数。例如:9->3->7,可以代表整数937。
例如:链表1位9->3->7,链表2为6->3,最后生成新的结果链表为1->0->0->0。
思路:先将链表转换成整数,求和,再转换成链表。这种方式转int类型时可能会溢出,所以不建议这种方法。
//利用栈结构求解。该方法空间复杂度较高,可以利用逆序链表求解
public Node addList(Node head1,Node head2) {
Stack<Integer> s1 = new Stack<Integer>();
Stack<Integer> s2 = new Stack<Integer>();
Node cur=head1;
while(cur!=null) {
s1.push(cur.value);
cur=cur.next;
}
cur=head2;
while(cur!=null) {
s2.push(cur.value);
cur=cur.next;
}
Node node=null;
Node pre=null;
int ca=0;
int n=0;
int n1=0;
int n2=0;
while(!s1.isEmpty() || !s2.isEmpty()) {
n1= (s1.isEmpty())?0:s1.pop();
n2= (s2.isEmpty())?0:s2.pop();
n=n1+n2+ca;
pre=node;
node = new Node(n%10);
node.next=pre;
ca=n/10;
}
if(ca==1) {
pre=node;
node=new Node(1);
node.next=pre;
}
return node;
}
九、将单链表的每K个节点逆序
给定一个单链表的头节点head,实现一个调整单链表的函数,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。
例如:
1->2->3->4->5->6->7->8->null,K=3。
调整后:3->2->1->6->5->4->7->8->null。其中7/8不调整,因为不够一组。
//将单链表的每K个节点之间逆序
public Node reverseKNodes(Node head, int K) {
if (K<2) {
return head;
}
Stack<Node> stack = new Stack<Node>();
Node cur = head;
Node pre = null;
Node next = null;
Node newHead = head;
while (cur!=null) {
next = cur.next;
stack.push(cur);
if (stack.size() == K) {
pre = reverse(stack, pre, next);
newHead = newHead==head?cur:newHead;
}
cur = next;
}
return newHead;
}
public Node reverse(Stack<Node> stack, Node left, Node right) {
Node cur = stack.pop();
if(left != null) {
left.next=cur;
}
Node next = null;
while (!stack.isEmpty()) {
next = stack.pop();
cur.next = next;
cur = next;
}
cur.next = right;
return cur;
}
十、删除无序单链表中值重复出现的节点
给定一个无序单链表的头节点head,删除其中值重复出现的节点。
例如:1->2->3->3->4->4->2->1->1->null,删除值重复的节点之后为1->2->3->4->null。
方法一:时间复杂度为O(1)。
方法二:额外空间复杂度为O(1)。
//方法一:删除无序单链表中值重复出现的节点
public void removeRep1(Node head) {
if(head == null) {
return;
}
Node pre = head;
Node cur = pre.next;
HashSet<Integer> set = new HashSet<>();
set.add(head.value);
while(cur!=null) {
if(set.contains(cur.value)) {
pre.next = cur.next;
} else {
set.add(cur.value);
pre = cur;
}
cur = cur.next;
}
}
//方法二:删除无序单链表中值重复出现的节点
public void removeRep2(Node head) {
Node cur = head;
Node pre = null;
Node next = null;
while(cur!=null) {
pre = cur;
next = pre.next;
while(next!=null) {
if(cur.value == next.value) {
pre.next = next.next;
}else {
pre = next;
}
next = next.next;
}
cur = cur.next;
}
}