線性表的基礎概念和結構請參考上一篇,ListNode 結構請參考上一篇,本篇討論線性表的幾個算法,開闊算法思路。
線性表反轉算法
算法描述:線性表反轉,就是由原來的前一個節點指向後一個節點,變成後一個節點指向前一個節點,例如1,2,3,4的順序,反轉成4,3,2,1。
/**
* 反轉鏈表
* 時間複雜度O(n),空間複雜度O(1)
* @param head
* @return
*/
public static ListNode reverseList(ListNode head) {
ListNode pre = null;//上一個節點
ListNode next = null;//下一個節點
while (head != null) {
next = head.next;//獲取下一個節點,記錄下來
head.next = pre;//把當前節點反轉指向上一個節點
pre = head;//上一個節點後移一位
head = next;//當前節點後移一位,處理下一個節點
}
return pre;
}
線性表取中間節點
算法描述:如果線性表是1,2,3,4,5,則算法輸出的是3的節點,如果是1,2,3,4,算法輸出的2。假設定義兩個變量,一個一次增加2,一個一次增加1,那麼當第一個變量走到末尾的時候,第二個變量剛好到中間。
/**
* 取中間節點(偶數取得中間節點前面得那個)
* 時間複雜度O(2/n),空間複雜度O(1)
* @param head
*/
public static ListNode getMid(ListNode head) {
if (head == null) {
return head;
}
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
兩個有序線性表合併
兩個有序線性表,如一個是1,3,5另一個是2,4,6,合併之後是1,2,3,4,5,6.
遞歸算法
/**
* 採用遞歸
* @param head1
* @param head2
* @return
*/
public static ListNode mergeTwoList(ListNode head1, ListNode head2) {
if (head1 == null && head2 == null) {
return null;
}
if (head1 == null) {
return head2;
}
if (head2 == null) {
return head1;
}
ListNode head = null;
if (head1.value > head2.value) {//說明第一個值是取得head2,就要把head2往後移,遞歸
head = head2;
head.next = mergeTwoList(head1, head2.next);
} else { //說明第一個值是取得head1,就要把head1往後移,遞歸
head = head1;
head.next = mergeTwoList(head1.next, head2);
}
return head;
}
非遞歸算法
/**
* 非遞歸
* @param head1
* @param head2
* @return
*/
public static ListNode mergeTwoList2(ListNode head1, ListNode head2) {
if (head1 == null || head2 == null) {
return head1 != null ? head1 : head2;
}
//存儲排序後的鏈表
ListNode head = head1.value < head2.value ? head1 : head2;
//記錄head1
ListNode cur1 = head == head1 ? head1 : head1;
//記錄head2
ListNode cur2 = head == head1 ? head2 : head1;
ListNode pre = null;//cur1前一個元素
ListNode next = null;//cur2後一個元素
while (cur1 != null && cur2 != null) {
if (cur1.value <= cur2.value) {
pre = cur1;
cur1 = cur1.next;
} else {
// 把cur2合併到cur1,相當於插入操作
next = cur2.next;
pre.next = cur2;
cur2.next = cur1;
pre = cur2;
cur2 = next;
}
}
//判斷是不是其中一個已經結束
pre.next = cur1 == null? cur2:cur1;
return head;
}
面試真題
鏈表排序
描述:一個鏈表,奇數位升序,偶數位降序,對鏈表進行排序,例如:183654729,排序後123456789,要求,空間複雜度O(n);
思路:
1.按照奇數位和偶數位拆分成兩個鏈表
2.對偶數位反轉
3.將兩個有序鏈表進行合併
按照如上的思路,是不是把問題分解成了幾個簡單的鏈表操作。直接上代碼:
/**
* 分三步
* 1.按照奇數位和偶數位拆分成兩個鏈表
* 2.對偶數位反轉
* 3.將兩個有序鏈表進行合併
*/
public static ListNode[] getLists(ListNode head) {
ListNode head1 = null;
ListNode head2 = null;
ListNode cur1 = null;
ListNode cur2 = null;
int count = 1;
while( head != null) {
if (count % 2 == 1) {
if (cur1 != null) {
cur1.next = head;
cur1 = cur1.next;
} else {
cur1 = head;
head1 = cur1;
}
} else {
if (cur2 != null) {
cur2.next = head;
cur2 = cur2.next;
} else {
cur2 = head;
head2 = cur2;
}
}
head = head.next;
count++;
}
cur1.next = null;
cur2.next = null;
ListNode[] nodes = new ListNode[] {head1, head2};
return nodes;
}
/**
* 鏈表2反轉
* @param head
* @return
*/
public static ListNode reverseList(ListNode head) {
ListNode pre = null;//上一個節點
ListNode next = null;//下一個節點
while (head != null) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
/**
* 採用遞歸合併
* @param head1
* @param head2
* @return
*/
public static ListNode mergeTwoList(ListNode head1, ListNode head2) {
if (head1 == null && head2 == null) {
return null;
}
if (head1 == null) {
return head2;
}
if (head2 == null) {
return head1;
}
ListNode head = null;
if (head1.value > head2.value) {
head = head2;
head.next = mergeTwoList(head1, head2.next);
} else {
head = head1;
head.next = mergeTwoList(head1.next, head2);
}
return head;
}