文章目錄
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;
}
}