實(shí)現(xiàn)一種算法,找出單向鏈表中倒數(shù)第 k 個(gè)節(jié)點(diǎn)。返回該節(jié)點(diǎn)的值。
使用swift語言解決:
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init(_ val: Int) {
* self.val = val
* self.next = nil
* }
* }
*/
class Solution {
func kthToLast(_ head: ListNode?, _ k: Int) -> Int {
var fast = head
// 從1開始到k,那應(yīng)該包括k
for _ in 1 ... k {
fast = fast?.next
}
var slow = head
while fast != nil {
fast = fast?.next
slow = slow?.next
}
return slow?.val ?? 0
}
}
主要是通過快慢指針快速計(jì)算。快指針先把要倒計(jì)的次數(shù)過掉(1到k,個(gè)數(shù)為k),這樣進(jìn)行指針向后偏移時(shí),偏移次數(shù)就剛好是從正向開始數(shù)到要倒計(jì)的位置(n - k),這樣再結(jié)合一個(gè)全鏈表,讓它跟著快指針偏移,這樣就剛好得到想要的,當(dāng)然最好用筆畫一下更容易理解。
以下的思路是開始的時(shí)候做的:很明顯,時(shí)間復(fù)雜度要高于上面的。
func kthToLast(_ head: ListNode?, _ k: Int) -> Int {
var count = 0
var head1 = head
if head1 != nil {
count += 1
}
while head1?.next != nil{
count += 1
head1 = head1?.next
}
if count == 0 {
return 0
}
var tem = 0
head1 = head
let index = (count - k)
while tem != index {
head1 = head1?.next
tem += 1
}
return head1?.val ?? 0
}
實(shí)現(xiàn)一種算法,刪除單向鏈表中間的某個(gè)節(jié)點(diǎn)(即不是第一個(gè)或最后一個(gè)節(jié)點(diǎn)),假定你只能訪問該節(jié)點(diǎn)。
解題思路:由于單向鏈表,無法訪問前置節(jié)點(diǎn),所有是無法真實(shí)刪除掉當(dāng)前節(jié)點(diǎn)的,可以采取通過把下一節(jié)點(diǎn)的值賦值給當(dāng)前節(jié)點(diǎn),更新當(dāng)前節(jié)點(diǎn)的值達(dá)到刪除當(dāng)前節(jié)點(diǎn)的值的目標(biāo)。
使用java解決:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}
將兩個(gè)升序鏈表合并為一個(gè)新的 升序 鏈表并返回。新鏈表是通過拼接給定的兩個(gè)鏈表的所有節(jié)點(diǎn)組成的。
解題思路:核心代碼需要指定一個(gè)新鏈表,同時(shí)增加一個(gè)當(dāng)前指針指向鏈表頭,然后當(dāng)前指針為新增節(jié)點(diǎn)不斷后移,表頭指針還在,這樣就可以獲取到整個(gè)鏈表的數(shù)據(jù)元素。
// 鏈表 頭指針
let node = ListNode(0)
// 當(dāng)前指針
var cur = node
// 新增節(jié)點(diǎn)
let node1 = ListNode(1)
// 鏈表的下一個(gè)節(jié)點(diǎn)指定是新增節(jié)點(diǎn)
cur.next = node1
// 當(dāng)前指針更新為指向新增節(jié)點(diǎn)
cur = cur.next
具體解決:
func mergeTwoLists(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? {
var l11 = l1
var l22 = l2
let node = ListNode(0)
var cur = node
while l11 !=
nil && l22 != nil {
if let v1 = l11?.val, let v2 = l22?.val {
if v1 > v2 {
cur.next = l22
cur = cur.next ?? ListNode(0)
l22 = l22?.next
}else {
cur.next = l11
cur = cur.next ?? ListNode(0)
l11 = l11?.next
}
}
}
if l11 == nil {
cur.next = l22
}else {
cur.next = l11
}
return node.next
}
注意:鏈表倒置鏈表核心代碼,
var pre:ListNode? = nil
while fast != nil{
let temp = pre
// 這里相當(dāng)于新創(chuàng)建一個(gè)節(jié)點(diǎn),如果用head節(jié)點(diǎn)賦值,會讓p的next有指向。
pre = ListNode(head?.val ?? 0)
// 頭插法 反轉(zhuǎn)
pre?.next = temp
fast = fast?.next
}
真正的倒置應(yīng)該是把當(dāng)前節(jié)點(diǎn)指針改成指向前面節(jié)點(diǎn)。只是通過指針的變化,不新增節(jié)點(diǎn),是最好的,改變原鏈表是必須的。
var head1 = head;
var nhead: ListNode? = nil;
while head1 != nil {
// 臨時(shí)存儲,不需要全局存儲,一個(gè)循環(huán)就重新賦值
let ntemp = nhead;
// 把原鏈表當(dāng)前要處理的節(jié)點(diǎn)賦給新鏈表的頭節(jié)點(diǎn),也就是原鏈表當(dāng)前要處理的節(jié)點(diǎn)多了一個(gè)新鏈表的頭指針指向它。
nhead = head1
//接著把原鏈表的當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)用原節(jié)點(diǎn)頭指針緩存,因?yàn)橄乱徊綍淖儺?dāng)前節(jié)點(diǎn)的下一節(jié)點(diǎn)的內(nèi)容為前一節(jié)點(diǎn),防止下一個(gè)節(jié)點(diǎn)找不到,自然需要緩存,用頭節(jié)點(diǎn)即可,無需臨時(shí)指針。
head1 = head1?.next;
//頭插法,把當(dāng)前節(jié)點(diǎn)的下節(jié)點(diǎn)指向新鏈表緩存的最新節(jié)點(diǎn)。這樣就完成了倒置(反轉(zhuǎn))鏈表
nhead?.next = ntemp;
}
請判斷一個(gè)鏈表是否為回文鏈表。
兩種解法;
- 采取棧來處理
int count = 0;
if (head == null) {
return true;
}
ListNode temp = head;
while (temp != null) {
count ++;
temp = temp.next;
}
Stack<Integer> stack = new Stack();
temp = head;
int i = 0;
boolean flag = true;
for (i = 0; i < count /2; i++) {
stack.push(Integer.valueOf(temp.val));
temp = temp.next;
}
if (count% 2 != 0 ) {
temp = temp.next;
}
ListNode temp1 = temp;
while (!stack.empty()) {
Integer tww = stack.pop();
try {
if (temp.val != tww) {
flag = false;
break;
}else {
temp = temp.next;
}
}catch (NullPointerException e) {
flag = false;
break;
}
}
return flag;
利用快慢指針處理,快指針向后兩位移動,慢指針按正常一個(gè)一個(gè)移動。遍歷快指針就可以根據(jù)慢指針取得中間位置。遍歷過程中如果加上一個(gè)指針p把前半鏈表值倒置,那最后就只需要遍歷慢指針,就可以比較p和慢指針節(jié)點(diǎn)的值判斷。
func isPalindrome(_ head: ListNode?) -> Bool {
if head == nil {
return true
}
var fast = head
var slow = head
// 用來存儲前半部分反轉(zhuǎn)鏈表的指針
var p:ListNode? = nil
while fast != nil && fast?.next != nil {
fast = fast?.next?.next
let temp = p
p = slow
slow = slow?.next
// 頭插法 反轉(zhuǎn)
p?.next = temp
}
while fast != nil {
let temp = p
p = slow
p?.next = temp
}
var flag = true
while p != nil {
if p?.val != slow?.val {
flag = false
break
}
slow = slow?.next
}
return flag
}
劍指 Offer 52. 兩個(gè)鏈表的第一個(gè)公共節(jié)點(diǎn)
問題思路需要捋一下。