Leetcode鏈表類算法題目分析總結 C++ Python實現

Leetcode鏈表類算法題目分析總結與C++ Python實現

鏈表是常見的數據結構,一般由關鍵字和成員指針構成,爲動態集合提供了一種簡單而靈活的表示方法,其中單鏈表,由於結構簡單而問題變化多樣,在程序員技術筆、面試中最爲常見,可以作爲試金石快速考察被面試者的coding能力。筆者整理了leetcode和麪試中常見的鏈表算法題,分類分析思路,力求舉一反三與融會貫通,同時給出C++、Python的實現,方便大家夯實算法基礎與準備算法刷題。

0. 鏈表的定義

  • 鏈表與數組比較,都是線性存儲結構,區別是數組的線性順序由下標決定,鏈表的順序是由各個對象裏的指針決定。
  • 雙向鏈表的每個元素都是一個對象(C++ Primer中對對象的解釋爲具有類型的內存區域),每個對象有一個關鍵字和兩個指針,還可以包含其他的數據。
  • 鏈表分爲單鏈接(singly linked)的和雙鏈接(double linked)的,已排序或未排序。
  • 鏈表元素的實現如下,

C++代碼

//C++版鏈表結點定義
struct Node {
  Node(int data, Node* next=nullptr) {
    v=data;
    n=next;
 }
 int v;
 Node* n;
}

python代碼

#python
#python3類定義可以省略顯式繼承object
class Node():
  def __init__(self, data, next=None):
    self.val=data
    #python無指針,變量均爲引用形式,引用可以近似爲指針,用引用代替指針
    self.next=next

1. 單鏈表的基本操作:建、查、插、改、刪

1.1 鏈表的創建有頭插法和尾插法兩種

/* params
pHead:鏈表頭指針的指針,創建的鏈表作爲參數返回,若使用指針則必須使用引用形式;也可以將頭指針做函數返回值
n: 鏈表長度
pdata: 若非空,則將數組元素順序作爲關鍵字
*/
void CreateLinklist(Node **pHead, int n, int *pdata = nullptr) {
  if (n < 1) {
    return;
  }
  if (pdata) {
    *pHead = new Node(pdata[0]);
  } else {
    *pHead = new Node(0);
  }
  Node *p = *pHead;
  for (int i = 1; i < n; i++) {
    Node *tmp;
    if (pdata) {
      tmp = new Node(pdata[i]);
    } else {
      tmp = new Node(i);
    }
    p->n = tmp;
    p = tmp;
  }
}

//頭插法
Node *CreateLinklist(int data[], int n) {
  if (!data) {
    return nullptr;
  }
  Node *rear = new Node(data[n - 1]);
  Node *head;
  //頭插法
  for (int i = n - 2; i >= 0; i--) {
    head = new Node(data[i]);
    head->n = rear;
    rear = head;
  }
  return head;
}

1.2 鏈表的查找

Node *ListSearch(Node* head, int key) {
  Node *p=head;
  while(p){
    if(p->v==key){
      return p;
    }
    p=p->n;
  }
  return p;
} 

1.3 鏈表的插入、刪除

void InsertNode(Node **pHead, int Index, int Value) {
  if (Index < 0) {
    return;
  }
  Node *tmp = new Node(Value);
  if (Index == 0) {
    tmp->n = *pHead;
    *pHead = tmp;
    return;
  }
  int cnt = Index;
  Node *p = *pHead;
  while (cnt > 1 && p) {
    p = p->n;
    cnt--;
  }
  tmp->n = p->n;
  p->n = tmp;
}

//刪除鏈表位置Index所在的節點
void DeleteNode(Node **pHead, int Index) {
  if (Index == 1) {
    Node *p = *pHead;
    *pHead = (*pHead)->n;
    delete p;
    return;
  }
  if (Index > 1) {
    int cnt = Index;
    Node *p = *pHead;
    while (cnt > 2 && p) {
      p = p->n;
      cnt--;
    }
    Node *cur = p->n;
    p->n = cur->n;
    delete cur;
  }
}
//刪除整個鏈表
void DeleteLinkedList(Node **pHead) {
  if (*pHead == nullptr) {
    return;
  }
  Node *p = *pHead;
  Node *tmp = *pHead;
  while (p) {
    tmp = p;
    p = p->n;
    delete tmp;
  }
}

2. 常見單鏈表算法題

建議選擇一個簡單數據,按照代碼逐行分析變量,畫出鏈表圖分析,熟能生巧。
2.1 題目:反轉一個單鏈表
可以採用遞歸思路實現,將當前結點以後的鏈表完成反轉,再把當前結點的下一個結點的next指向當前結點,當前結點的next指針置空。

Node *ReverseLinkedList(Node *head) {
  if (!head || !head->n) {
    return head;
  }
  Node *newhead = ReverseLinkedList(head->n);
  head->n->n = head;
  head->n = nullptr;
  return newhead;
}

考慮非遞歸方式如何實現,可以想象在平地上移動一艘船該如何做呢,取用多個圓木,依次墊底,從後往前放置,爲了能夠修改整個鏈表的順序需要三個指針來完成這個目的。

//對參數傳入的鏈表進行逆轉
void ReverseLinkedList(Node **pHead) {
  if (*pHead == nullptr || (*pHead)->n == nullptr) {
    return;
  }
  Node *pre = nullptr;
  Node *cur = *pHead;
  Node *n = (*pHead)->n;
  while (cur) {
    n = cur->n;
    cur->n = pre;
    pre = cur;
    cur = n;
  }
  *pHead = pre;
}

用三個指針當作傳遞的圓木流動,也是解決鏈表的插入排序問題一個方法。
2.2 題目:鏈表上實現插入排序(Leetcode )
插入排序的基本思路是,每次從未排序的右側元素去一個,插入到已經排好序的左側的適當位置,相比對數組做插入排序,更加直觀;需要考慮待插入元素是最小的,即要把這個結點放到頭,所以需要構造一個關鍵字爲無窮小的頭指針dum,每次循環前pre置爲頭指針,cur爲排好序的部分的尾結點,lat爲待插入元素,循環中pre移動到待插入結點的左側,並用tmp標出插入位置,插入結點,並保持鏈表完整。

void InsertSort(Node *&pHead) {
  if (!pHead || !pHead->n) {
    return;
  }
  Node *dum = new Node(INT_MIN);
  dum->n = pHead;
  Node *pre = dum;
  Node *cur = pHead;
  Node *lat = pHead->n;
  while (cur) {
    lat = cur->n;
    if (lat && cur->v > lat->v) {
      while (pre->n && pre->n->v < lat->v) {
        pre = pre->n;
      }
      Node *tmp = pre->n;
      pre->n = lat;
      cur->n = lat->n;
      lat->n = tmp;
    } else {
      cur = lat;
    }
    pre = dum;
  }
  pHead = dum->n;
}

2.3 兩個鏈表做十進制加法

Node *AddTwoNumbers(Node *l1, Node *l2) {
  Node *dummy = new Node(0), *p = dummy;
  int carry = 0;
  while (l1 || l2 || carry) {
    if (l1) {
      carry += l1->v;
      l1 = l1->n;
    }
    if (l2) {
      carry += l2->v;
      l2 = l2->n;
    }
    p->n = new Node(carry % 10);
    carry /= 10;
    p = p->n;
  }
  return dummy->n;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章