今天跟着左程云算法课007-012,07和08讲了【时空复杂度】和【算法与数据结构的概念】,简单复习了一下。重点在于009-012课程链表类题目的学习。总结记录一下,以下是单链表结点的通用代码:
//单链表节点
public static class ListNode{
public int val;
public ListNode nextListNode;
public ListNode(int n) {
this.val=n;
}
public ListNode(int n,ListNode next) {
this.val=n;
this.nextListNode=next;
}
}
一、单链表和双链表的逆置
(1)单链表逆置
链表逆置是链表类最基础的算法。head头结点要移动,除了head指向当前结点之外,要另外设置两个变量pre和next分别指向当前结点的前一个结点和后一个结点。
public static ListNode ListReverse(ListNode head) {
ListNode pre=null;
ListNode next=null;
while(head!=null) {
next=head.nextListNode;
head.nextListNode=pre;
pre=head;
head=next;
}
return pre;
}
(2)双链表的逆置
双链表的逆置和单链表逆置思路一样,只是在当年节点的next变动时要pre和next一起变动,思路和上述的单链表逆置一模一样。
//双链表节点
public static class DoubleListNode{
public int val;
public DoubleListNode pre;
public DoubleListNode next;
public DoubleListNode(int n) {
this.val=n;
}
public DoubleListNode(int n, DoubleListNode pre, DoubleListNode next) {
this.val=n;
this.pre=pre;
this.next=next;
}
}
//逆置双链表
public static DoubleListNode reverDoubleListNode(DoubleListNode head) {
DoubleListNode pre=null;
DoubleListNode next=null;
while(head!=null) {
next=head.next;
head.next=pre;
head.pre=next;
pre=head;
head=next;
}
return pre;
}
二、有序链表的合并
对于有序链表的合并,进行边缘条件判定之后,比较两个链表头结点大小,然后将返回的head指向小头,这里的head之后不变,用于返回合并链表的头。之后设置一个cur指向合并链表的最后一个结点(也就是说只要有一个结点兜进来,就用cur指向这个结点),分别用cur1和cur2在两个链表上移动。比较cur1和cur2的大小,哪个小哪个链到cur后面,直到其中一个链表走完。走完后接着把没链上的链进来,然后返回最开的head结点。
代码测试链接:. - 力扣(LeetCode)
public static ListNode MergeList(ListNode h1,ListNode h2) {
if(h1==null||h2==null) {
return h1==null?h2:h1;
}
ListNode head=h1.val>h2.val?h2:h1;
ListNode cur=head;
ListNode cur1=h1.val>h2.val?h1:h1.next;
ListNode cur2=h1.val>h2.val?h2.next:h2;
while(cur1!=null && cur2!=null) {
if(cur1.val<cur2.val) {
cur.next=cur1;
cur1=cur1.next;
}
else {
cur.next=cur2;
cur2=cur2.next;
}
cur=cur.next;
}
if(cur1!=null || cur2!=null) {
cur.next= cur1==null?cur2:cur1;
}
return head;
}
三、两个链表相加
对于两个链表相加,要返回的数据类型也是NodeList,所以要重开一个链表来存储相加结果,用ans返回结果链表头,用cur来指向结果链表尾。写一个循环开始加,循环条件是h1或h2不为空即可。相加得到sum的时候要把h1、h2和carry都考虑上,加出来的结果处理后往结果链表放。代码如下。
代码测试链接:. - 力扣(LeetCode)
public static ListNode AddTwoNumbers(ListNode h1,ListNode h2) {
ListNode ans=null,cur=null;
int carry=0;
for(int sum,val;
h1!=null||h2!=null;
h1= h1==null?null:h1.next,
h2= h2==null?null:h2.next) {
sum=(h1==null?0:h1.val)+(h2==null?0:h2.val)+carry;
val=sum%10;
carry=sum/10;
if(ans==null) {
ans=new ListNode(val);
cur=ans;
}
else {
cur.next=new ListNode(val);
cur=cur.next;
}
}
if(carry==1) {
cur.next=new ListNode(1);
}
return ans;
}
四、链表划分
这个题目要以x为分界点,分成左右两个区域。那么我们就设置4个变量,分别是smallHead,smallTail,表示小头和小尾。bigHead和bigTail,表示大头和大尾。然后设置一个cur,我们在对于当下节点head的时候,用cur指向head.next,然后后面发货的时候把head.next断开,方便当前head发货完还能接着原来的顺序往后遍历继续走。思路很简单,head拿到一个数,判断然后发货,发往大区或者小区。最后要注意判断一下小区里面是否有元素存在,如果没有直接返回大头,如果有元素的话,把小尾和大头连接起来然后返回小头。代码如下:
代码测试链接:. - 力扣(LeetCode)
public static ListNode PartList(ListNode head,int x) {
ListNode smallHead=null,smallTaiListNode=null;
ListNode bigHead=null,bigTaiListNode=null;
ListNode cur=null;
if(head==null) {
return null;
}
while(head!=null) {
cur=head.next;
head.next=null;
//发货
if(head.val<x) {
//发货给小区
if(smallHead==null) {
smallHead=head;
}
else {
smallTaiListNode.next=head;
}
smallTaiListNode=head;
}
else {
//发货给大区
if(bigHead==null) {
bigHead=head;
}
else{
bigTaiListNode.next=head;
}
bigTaiListNode=head;
}
head=cur;
}
if(smallHead==null) {
return bigHead;
}
else {
smallTaiListNode.next=bigHead;
return smallHead;
}
}