從尾到頭打印鏈表
blic ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer>list=new ArrayList<>();
if(listNode==null)return list;
while(listNode!=null){
list.add(0,listNode.val);
listNode=listNode.next;
}
return list;
}
鏈表中倒數第k個結點
先走k步,如果中途爲空,說明鏈表長度小於k,則返回null
public ListNode FindKthToTail(ListNode head,int k) {
if(head==null)
return null;
ListNode a=head;
ListNode b=head;
for(int i=0;i<k;i++){
if(a!=null){
a=a.next;
}else{
return null;
}
}
while(a!=null){
b=b.next;
a=a.next;
}
return b;
}
反轉鏈表
public ListNode ReverseList(ListNode head) {
if(head==null)return null;
ListNode pre=head;
ListNode result=null;
while(pre!=null){
ListNode next=pre.next;
pre.next=result;
result=pre;
pre=next;
}
return result;
}
合併兩個排序的鏈表
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null)return list2;
if(list2==null)return list1;
ListNode node=new ListNode(0);
ListNode result=node;
ListNode pre1=list1;
ListNode pre2=list2;
while(pre1!=null&&pre2!=null){
if(pre1.val<=pre2.val){
result.next=pre1;
result=pre1;
pre1=pre1.next;
}else{
result.next=pre2;
result=pre2;
pre2=pre2.next;
}
}
if(pre1!=null)result.next=pre1;
if(pre2!=null)result.next=pre2;
return node.next;
}
刪除鏈表的重複結點
用三個指針遍歷
public ListNode deleteDuplication(ListNode pHead)
{
if(pHead==null)return null;
ListNode node=new ListNode(0);
node.next=pHead;
ListNode pre1=node;
ListNode pre2=pHead;
ListNode pre3=pHead.next;
while(pre3!=null){
if(pre2.val!=pre3.val){
pre1=pre1.next;
pre2=pre2.next;
pre3=pre3.next;
}else{
while(pre3!=null&&pre2.val==pre3.val){
pre3=pre3.next;
}
pre1.next=pre3;
pre2=pre3;
if(pre3!=null){
pre3=pre3.next;
}
}
}
return node.next;
}
鏈表中環的入口結點
- 判斷鏈表是否帶環,用快慢指針,如果相遇說明有環。
- 一個指針從頭開始,一個從相遇結點開始,直到它們兩相遇,就是環的入口結點
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead==null)return null;
ListNode p1=pHead;
ListNode p2=pHead;
do{
p1=p1.next;
if(p1==null)
return null;
p1=p1.next;
p2=p2.next;
}while(p1!=p2);
ListNode p3=pHead;
while(p1!=p3){
p1=p1.next;
p3=p3.next;
}
return p3;
}
兩個鏈表的第一個公共結點
思路一:求出兩個鏈表的長度,找出長的鏈表,讓長的鏈表走長度差,再讓兩個一起走,直到相遇。
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1==null)return null;
if(pHead2==null)return null;
ListNode p1=pHead1;
ListNode p2=pHead2;
int len1=getLength(pHead1);
int len2=getLength(pHead2);
int k=0;
if(len1<len2){
p1=pHead2;
p2=pHead1;
k=len2-len1;
}
k=len1-len2;
for(int i=0;i<k;i++){
p1=p1.next;
}
while(p1!=p2){
p1=p1.next;
p2=p2.next;
}
return p2;
}
public static int getLength(ListNode phead){
if(phead==null)return 0;
int count=0;
ListNode p=phead;
while(p!=null){
count++;
p=p.next;
}
return count;
}
思路二:相當於遍歷一遍兩個鏈表的長度和即可
例如:
l1:0-1-2-3-4-5-null
l2:a-b-4-5-null
p1: 0-1-2-3-4-5-null(此時遇到ifelse)-a-b-4-5-null
p2: a-b-4-5-null(此時遇到ifelse)0-1-2-3-4-5-null
因此,兩個指針所要遍歷的鏈表就長度一樣了!
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1==null)return null;
if(pHead2==null)return null;
ListNode p1=pHead1;
ListNode p2=pHead2;
while(p1!=p2){
p1=p1==null?pHead2:p1.next;
p2=p2==null?pHead1:p2.next;
}
return p1;
}
二叉搜索樹與雙向鏈表
用的是中序遍歷非遞歸版本
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree==null)return null;
Stack<TreeNode>stack=new Stack<>();
TreeNode pre=pRootOfTree;
TreeNode node=null;
boolean bool=true;
while(!stack.isEmpty()||pre!=null){
while(pre!=null){
stack.push(pre);
pre=pre.left;
}
TreeNode top=stack.pop();
if(bool){
pRootOfTree=top;//讓第一個結點作爲頭
node=top;
bool=false;
}else{
node.right=top;
top.left=node;
node=top;
}
pre=top.right;
}
return pRootOfTree;
}
複雜鏈表的複製
public RandomListNode Clone(RandomListNode pHead)
{
if(pHead==null)return null;
RandomListNode node=pHead;
//合併
while(node!=null){
RandomListNode a=new RandomListNode(node.label);
a.random=null;
a.next=node.next;
node.next=a;
node=a.next;
}
//算random
RandomListNode pre1=pHead;
while(pre1!=null){
RandomListNode pre2=pre1.next;
if(pre1.random!=null){
pre2.random=pre1.random.next;
}
pre1=pre2.next;
}
//拆分
pre1=pHead;
RandomListNode result=pHead.next;
while(pre1!=null){
RandomListNode p=pre1.next;
pre1.next=p.next;
if(p.next!=null){
p.next=p.next.next;
}
pre1=pre1.next;
}
return result;
}
重排鏈表
方法一:找到中間結點,將尾部鏈表逆置,然後遍歷兩個鏈表,交叉插入結點。
方法二:將結點放入到隊列中,方便取兩邊的結點。注意最後要設置空結點。
public void reorderList(ListNode head) {
LinkedList<ListNode>queue=new LinkedList<>();
if(head==null)return;
ListNode pre=head;
while(pre!=null){
queue.addLast(pre);
pre=pre.next;
}
while(!queue.isEmpty()){
if(pre==null){
pre=queue.pollFirst();
}else{
pre.next=queue.pollFirst();
pre=pre.next;
}
pre.next=queue.pollLast();
pre=pre.next;
}
if(pre!=null)
pre.next=null;
}
分割鏈表
將小於x的放在一個鏈表中,大於等於x的放到另外鏈表中,鏈表尾插比較麻煩,所以添加一個0元素的結點就不用每次去判斷頭結點爲不爲空。左後合併鏈表的時候,分爲三中情況。
public ListNode partition(ListNode head, int x) {
if(head==null)return null;
ListNode head1=new ListNode(0);
ListNode p1=head1;
ListNode head2=new ListNode(0);
ListNode p2=head2;
ListNode pre=head;
while(pre!=null){
if(pre.val<x){
p1.next=pre;
p1=p1.next;
pre=pre.next;
}else{
p2.next=pre;
p2=p2.next;
pre=pre.next;
}
}
if(p1==null)return head2.next;
else if(p2==null)return head1.next;
else{
p1.next=head2.next;
p2.next=null;
}
return head1.next;
}
鏈表求和
思想:遍歷兩個鏈表,carry用來存進位,每次算出一個數就添加一個新的結點,最後好需要判斷下carry是不是爲0,不爲0還要再添加一個結點。
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
if(l1==null)return l2;
if(l2==null)return l1;
ListNode result=new ListNode(0);
ListNode res=result;
int carry=0;
while(l1!=null||l2!=null){
int num1=l1==null?0:l1.val;
int num2=l2==null?0:l2.val;
int sum=num1+num2+carry;
carry=sum/10;
res.next=new ListNode(sum%10);
res=res.next;
l1=l1==null?null:l1.next;
l2=l2==null?null:l2.next;
}
if(carry>0){
res.next=new ListNode(carry);
}
return result.next;
}
旋轉鏈表
思路:首先算出鏈表的長度,計算出要循環的次數,也就是length-(k%length),然後將鏈表變成循環鏈表,一個從尾指針開始,一個從頭指針開始,遍歷次數完成,將尾指針的next設爲null,返回頭指針現在所在的位置,就是頭結點。
public ListNode rotateRight(ListNode head, int k) {
if(head==null||k==0)return head;
int length=1;
ListNode p1=head;
while(p1.next!=null){
p1=p1.next;
length++;
}
int top=length-(k%length);
ListNode p2=head;
//構建環形鏈表 p1是尾指針,p2是頭指針
p1.next=head;
for(int i=0;i<top;i++){
p1=p1.next;
p2=p2.next;
}
p1.next=null;
return p2;
}
奇偶鏈表
public ListNode oddEvenList(ListNode head) {
if(head==null)return head;
ListNode o=head;//head爲奇鏈表的頭結點,o爲奇鏈表的尾結點
ListNode p=head.next;//偶鏈表的頭結點
ListNode e=head.next;//偶鏈表的尾結點
while(o.next!=null&&e.next!=null){
o.next=e.next;
o=o.next;
e.next=o.next;
e=e.next;
}
o.next=p;
return head;
}
移除鏈表元素
有可能鏈表只有一個結點,並且這個結點值就是val,有可能val再結尾會出現,所以方便減少代碼判斷,先設置一個0的結點放在head的前面。這樣就不用考慮特殊情況了。
public ListNode removeElements(ListNode head, int val) {
if(head==null)return head;
ListNode node=new ListNode(0);
node.next=head;
ListNode pre=node;
while(pre.next!=null){
if(pre.next.val!=val){
pre=pre.next;}
else
pre.next=pre.next.next;
}
// if(pre.val==val)
// pre.next=null;
return node.next;
}
刪除鏈表中的結點
比如3–4--5,需要刪除4,讓4這個結點值爲5,再繞過5這個結點即可。
public void deleteNode(ListNode node) {
node.val=node.next.val;
node.next=node.next.next;
}
兩兩交換鏈表中的結點
三個指針進行交換
public ListNode swapPairs(ListNode head) {
if(head==null||head.next==null)return head;
ListNode node=new ListNode(0);
ListNode p1=node;
node.next=head;
while(head!=null&&head.next!=null){
ListNode p2=head;
ListNode p3=head.next;
p1.next=p3;
p2.next=p3.next;
p3.next=p2;
p1=p2;
head=p2.next;
}
return node.next;
}
鏈表的中間結點
public ListNode middleNode(ListNode head) {
ListNode a=head;
ListNode b=head;
while(a!=null&&a.next!=null){
a=a.next.next;
b=b.next;
}
return b;
}
k個一組反轉鏈表
public ListNode reverseKGroup(ListNode head, int k) {
ListNode node=new ListNode(0),prev=node,next,curry=head;
int length=0;
while(curry!=null){
length++;
curry=curry.next;
}
curry=head;
node.next=head;
for(int i=0;i<length/k;i++){
for(int j=0;j<k-1;j++){
next=curry.next;
curry.next=next.next;
next.next=prev.next;
prev.next=next;
}
prev=curry;
curry=curry.next;
}
return node.next;
}
合併k個排序鏈表
public ListNode mergeKLists(ListNode[] lists) {
if(lists.length==0)return null;
if(lists.length==1)return lists[0];
if(lists.length==2) return mergeTwo(lists[0],lists[1]);
int mid=lists.length/2;
ListNode[]l1=new ListNode[mid];
ListNode[]l2=new ListNode[lists.length-mid];
for(int i=0;i<mid;i++){
l1[i]=lists[i];
}
for(int j=0,i=mid;i<lists.length;j++,i++){
l2[j]=lists[i];
}
return mergeTwo(mergeKLists(l1),mergeKLists(l2));
}
private ListNode mergeTwo(ListNode p, ListNode q) {
if(p==null)return q;
if(q==null)return p;
ListNode result=new ListNode(-1);
ListNode res=result;
while(p!=null&&q!=null){
if(p.val<=q.val){
result.next=new ListNode(p.val);
result=result.next;
p=p.next;
}else{
result.next=new ListNode(q.val);
result=result.next;
q=q.next;
}
}
if(p!=null)result.next=p;
if(q!=null)result.next=q;
return res.next;
}