成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之鏈表

jerryloveemily / 1053人閱讀

摘要:本系列所有文章第一篇文章學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之棧與隊(duì)列第二篇文章學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之鏈表第三篇文章學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之集合第四篇文章學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之字典和散列表第五篇文章學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之二叉搜索樹簡(jiǎn)單介紹鏈表鏈表一種常見的數(shù)據(jù)結(jié)構(gòu),可

本系列所有文章:
第一篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之棧與隊(duì)列
第二篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之鏈表
第三篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之集合
第四篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之字典和散列表
第五篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之二叉搜索樹

簡(jiǎn)單介紹鏈表

鏈表一種常見的數(shù)據(jù)結(jié)構(gòu),可以存儲(chǔ)有序的元素集合。不同于數(shù)組,鏈表中元素在內(nèi)存中不是連續(xù)放置,同時(shí)每一個(gè)鏈表元素中除了本身的節(jié)點(diǎn)還保存了指向下一個(gè)元素的引用,這些特點(diǎn)使得鏈表元素在動(dòng)態(tài)增加和刪除時(shí)不必移動(dòng)其他元素,但是訪問鏈表元素時(shí)必須從起點(diǎn)開始迭代列表直到找到目標(biāo)元素。

下面是兩種鏈表的JavaScript實(shí)現(xiàn):

單向鏈表

下圖就是一個(gè)典型的單向鏈表,這里注意:一般鏈表中的第一個(gè)元素會(huì)有一個(gè)head指針指向它,這也是我們操作鏈表必不可少的一環(huán)。

(圖片來源谷歌,侵刪)

用JavaScript實(shí)現(xiàn)單向鏈表

與之前實(shí)現(xiàn)棧和隊(duì)列一樣,這里先聲明一個(gè)構(gòu)造函數(shù):

function LinkedList () {
  var Node = function (element) {
    this.element = element
    // 保存指向下個(gè)元素的引用,默認(rèn)為null
    this.next = null
  }
  
  // 鏈表長(zhǎng)度
  var length = 0
  // head保存指向第一個(gè)元素的引用
  var head = null
}

鏈表需要實(shí)現(xiàn)以下方法:

append(element):向鏈表尾部添加元素

insert(position, element):向鏈表特定位置插入元素

removeAt(position):從鏈表特定位置移除一項(xiàng)

remove(element):在鏈表中移除某元素

indexOf(element):返回元素在鏈表中的索引,若不存在則返回-1

isEmpty():如果鏈表不包含任何元素就返回true,否則為false

size():返回鏈表長(zhǎng)度

toString():返回元素的值轉(zhuǎn)成字符串

實(shí)現(xiàn)append

類似數(shù)組的push方法,但是只能添加一個(gè)元素。實(shí)現(xiàn)方法的時(shí)候分兩種情況考慮:1. 鏈表為空時(shí)添加第一個(gè)元素;2. 鏈表不為空時(shí)在尾部添加元素

this.append = function (element) {

  var node = new Node(element),
      current

  if (head === null) { // 當(dāng)鏈表為空時(shí)
    // 將head指向新增的元素
    head = node
  } else { // 鏈表不為空
    // 使用一個(gè)current變量從head開始迭代鏈表
    current = head

    // 迭代鏈表,直到找到最后一項(xiàng)
    while (current.next) {
      current = current.next
    }

    // 找到最后一項(xiàng),將其next賦為node,建立鏈接
    current.next = node
  }

  // 更新列表長(zhǎng)度
  length++
}
實(shí)現(xiàn)removeAt

在鏈表中特定位置移除元素,實(shí)現(xiàn)時(shí)也需要考慮兩種情況:1. 移除第一個(gè)元素;2. 移除其他元素(包括最后一個(gè))

this.removeAt = function (position) {
  // 判斷位置是否越界
  if (position > -1 && position < length) {
    var current = head,
        previous,
        index = 0

    // 如果刪除了第一個(gè)元素,把head指向下一個(gè)元素就行了
    if (position === 0) {
      head = current.next
    } else {
      // 根據(jù)輸入的位置查找要?jiǎng)h除的元素
      while (index++ < position) {
        previous = current
        current = current.next
      }
      // 將上一個(gè)元素的next指向current的下一項(xiàng),跳過current,實(shí)現(xiàn)移除current
      previous.next = current.next
    }

    // 更新列表長(zhǎng)度
    length--

    // 返回刪除的元素
    return current.element
  } else {
    return null
  }
}
實(shí)現(xiàn)insert

與removeAt類似的實(shí)現(xiàn),大家可以先不看源碼,自己按著removeAt的思路實(shí)現(xiàn)一遍

this.insert = function (position, element) {
  // 檢查位置是否越界
  if (position >= 0 && position <= length) {
    var node = new Node(element),
        index = 0,
        previous,
        current = head

    // 在第一個(gè)位置添加
    if (position === 0) {

      node.next = current
      head = node

    } else {
      while (index++ < position) {
        previous = current
        current = current.next
      }

      node.next = current
      previous.next = node
    }

    // 更新列表長(zhǎng)度
    length++

    return true
} else {
  return false
}
}
實(shí)現(xiàn)indexOf

根據(jù)元素查找在鏈表中的位置,沒找到就返回-1

this.indexOf = function (element) {
  var current = head,
      index = 0

  while (current) {
    if (element === current.element) {
      return index
    }
    index++
    current = current.next
  }

  return -1
}
實(shí)現(xiàn)其他方法
// 返回所有元素的值轉(zhuǎn)成字符串
this.toString = function () {
  var current = head,
      string = ""

  while (current) {
    string += current.element
    current = current.next
  }

  return string
}

// 移除特定元素
this.remove = function (element) {
  var index = this.indexOf(element)
  return this.removeAt(index)
}

// 判斷鏈表是否為空
this.isEmpty = function () {
  return length === 0
}

// 返回鏈表長(zhǎng)度
this.size = function () {
  return length
}

// 返回第一個(gè)元素
this.getHead = function () {
  return head
}

以上是單向鏈表的實(shí)現(xiàn),有興趣的可以下載源碼來看:

單向鏈表的實(shí)現(xiàn)-源代碼

雙向鏈表

雙向鏈表和單向鏈表的區(qū)別就是每一個(gè)元素是雙向的,一個(gè)元素中包含兩個(gè)引用:一個(gè)指向前一個(gè)元素;一個(gè)指向下一個(gè)元素。除此之外,雙向鏈表還有一個(gè)指向最后一個(gè)元素的tail指針,這使得雙向鏈表可以從頭尾兩個(gè)方向迭代鏈表,因此更加靈活。如下圖:

(圖片來源谷歌搜索,侵刪)

用JavaScript實(shí)現(xiàn)雙向鏈表

同單向鏈表一樣,先聲明一個(gè)構(gòu)造函數(shù)

function DoubleLinkedList () {
  var Node = function (element) {
    this.element = element
    this.prev = null // 新增了一個(gè)指向前一個(gè)元素的引用
    this.next = null
  }

  var length = 0
  var head = null
  var tail = null //新增了tail指向最后一個(gè)元素
}

雙向鏈表需要有以下方法:

append(element):向鏈表尾部添加元素

insert(position, element):向鏈表特定位置插入元素

removeAt(position):從鏈表特定位置移除一項(xiàng)

showHead():獲取雙向鏈表的頭部

showLength():獲取雙向鏈表長(zhǎng)度

showTail():獲取雙向鏈表尾部

實(shí)現(xiàn)insert

同單向鏈表類似,只不過情況更復(fù)雜了,你不僅需要額外考慮在第一個(gè)元素的位置插入新元素,還要考慮在最后一個(gè)元素之后插入新元素的情況。此外如果在第一個(gè)元素插入時(shí),鏈表為空的情況也需要考慮。

this.insert = function (position, element) {
  // 檢查是否越界
  if (position >= 0 && position <= length) {
    var node = new Node(element),
        current = head,
        previous,
        index = 0

    if (position === 0) { // 第一個(gè)元素的位置插入
      // 如果鏈表為空
      if (!head) {
        head = node
        tail = node
      } else {
        node.next = current
        current.prev = node
        head = node
      }
    } else if (position === length) { // 在最后一個(gè)元素之后插入
      current = tail
      node.prev = current
      current.next = node
      tail = node
    } else { // 在中間插入
      while (index++ < position) {
        previous = current
        current = current.next
      }

      node.next = current
      previous.next = node

      current.prev = node
      node.prev = previous
    }

    length++

    return true
  } else {
    return false
  }
}
實(shí)現(xiàn)removeAt

與insert類似,這里不多解釋直接貼代碼

this.removeAt = function (position) {
  // 檢查是否越界
  if (position > -1 && position < length) {
    var current = head,
        previous,
        index = 0

    if (position === 0) { // 第一個(gè)元素
      head = current.next
      // 如果只有一個(gè)元素
      if (length === 1) {
        tail = null
      } else {
        head.prev = null
      }
    } else if (position === length - 1) { // 最后一個(gè)元素
      current = tail
      tail = current.prev
      tail.next = null
    } else {
      while (index++ < position) {
        previous = current
        current = current.next
      }

      previous.next = current.next
      current.next.prev = previous
    }

    length--

    return current.element
  } else {
    return null
  }
}
實(shí)現(xiàn)append

和單向鏈表的一樣,只不過多了tail有一些不同

this.append = function (element) {
  var node = new Node(element),
      current = tail

  if (head === null) {
    head = node
    tail = node
  } else {
    node.prev = current
    current.next = node
    tail = node
  }

  length++
}

其他的代碼都很簡(jiǎn)單,我就放上源代碼地址,大家自行查閱吧~

雙向鏈表的實(shí)現(xiàn)-源代碼

小結(jié)

一開始寫的時(shí)候有點(diǎn)不習(xí)慣,但是實(shí)現(xiàn)了一兩個(gè)方法之后發(fā)現(xiàn)很多思路是類似的,于是舉一反三地寫出來,然后跟書上對(duì)比之后,發(fā)現(xiàn)還是有點(diǎn)差距的。

大家有興趣也動(dòng)手去實(shí)踐一下再對(duì)比源碼,可以認(rèn)識(shí)到自己有哪些地方不足。

鏈表暫時(shí)就這樣了,明天繼續(xù)攻克集合!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/87169.html

相關(guān)文章

  • 數(shù)據(jù)結(jié)構(gòu)算法隨筆鏈表-鏈表是否有環(huán)(二)

    摘要:初始化時(shí)指針走兩步,指針走一步,不停遍歷的變化最后快慢指針又相遇了,循環(huán)結(jié)束,代碼實(shí)現(xiàn)如下復(fù)雜度分析,假設(shè)鏈表長(zhǎng)度為時(shí)間復(fù)雜度,鏈表無環(huán)時(shí),快指針會(huì)先到達(dá)尾部,時(shí)間就是如果有環(huán),那么假設(shè)環(huán)部長(zhǎng)度為,時(shí)間就是,也就是空間復(fù)雜度 上一篇文章我們分析了下鏈表之反轉(zhuǎn)單向鏈表,這篇文章我們來分析下另外一個(gè)關(guān)于鏈表的經(jīng)典題目。 判斷鏈表是否有環(huán)(在leetcode上的題目地址:環(huán)形鏈表) 題目描述...

    molyzzx 評(píng)論0 收藏0
  • 利用PHP實(shí)現(xiàn)《劍指 offer》鏈表數(shù)據(jù)結(jié)構(gòu)算法實(shí)戰(zhàn) )

    摘要:一定要認(rèn)真看分析注釋題目要求題目描述輸入一個(gè)鏈表,從尾到頭打印鏈表每個(gè)節(jié)點(diǎn)的值。分析因?yàn)殒湵碇挥兄喇?dāng)前結(jié)點(diǎn)才能知道下一結(jié)點(diǎn),所以不可能直接從后往前打印。 一定要認(rèn)真看 分析 | 注釋 | 題目要求 Question 1 題目描述:輸入一個(gè)鏈表,從尾到頭打印鏈表每個(gè)節(jié)點(diǎn)的值。 分析:因?yàn)殒湵碇挥兄喇?dāng)前結(jié)點(diǎn)才能知道下一結(jié)點(diǎn),所以不可能直接從后往前打印。這種逆序的算法(策略)我們常用棧這...

    hiYoHoo 評(píng)論0 收藏0
  • PHPer也刷《劍指Offer》鏈表

    摘要:劍指中鏈表相關(guān)題目俗話說光說不練假把式,既然學(xué)習(xí)了鏈表的基礎(chǔ)概念和基本操作那我們一定要找些題目鞏固下,下面來看劍指中的相關(guān)題目。題目分析合并兩個(gè)排序的鏈表,需要分別比較兩個(gè)鏈表的每個(gè)值,然后改變指針。 溫故知新 鏈表由一個(gè)一個(gè)的作為節(jié)點(diǎn)的對(duì)象構(gòu)成的,每一個(gè)節(jié)點(diǎn)都有指向下一個(gè)節(jié)點(diǎn)的指針,最后一個(gè)節(jié)點(diǎn)的指針域指向空。每個(gè)節(jié)點(diǎn)可以存儲(chǔ)任何數(shù)據(jù)類型。 根據(jù)類型可以分為單鏈表、雙鏈表、環(huán)形鏈表、...

    daydream 評(píng)論0 收藏0
  • 03線性表鏈表

    摘要:帶頭雙向循環(huán)鏈表結(jié)構(gòu)最復(fù)雜,一般用在單獨(dú)存儲(chǔ)數(shù)據(jù)。實(shí)際中使用的鏈表數(shù)據(jù)結(jié)構(gòu),都是帶頭雙向循環(huán)鏈表。 肚子餓了就要吃? ?~? ?嗝? ——— 路飛? 1.本章重點(diǎn) 鏈表表示和實(shí)現(xiàn)(單鏈表+雙鏈表)鏈表的常見OJ題順序表和鏈表的區(qū)別和聯(lián)系2.為什么需要鏈表 引子:順序表的問題及思考 (1...

    jayce 評(píng)論0 收藏0
  • 利用PHP實(shí)現(xiàn)常用的數(shù)據(jù)結(jié)構(gòu)鏈表(小白系列文章五)

    摘要:因?yàn)樯婕爸羔槪覀冇靡脕砟M,所以讀者應(yīng)該有面向?qū)ο蟮闹R(shí)貯備。引文因?yàn)樯婕皟?nèi)存,常常會(huì)有一些程序的邊界限制,需要擁有一定嚴(yán)密的邏輯去保證代碼的魯棒性和健壯性,所以這個(gè)知識(shí)點(diǎn)是面試的??键c(diǎn)。 tips:因?yàn)樯婕爸羔?,我們用引用來模擬,所以讀者應(yīng)該有面向?qū)ο蟮闹R(shí)貯備。 引子 你可以把鏈表簡(jiǎn)單理解為動(dòng)態(tài)數(shù)組,它不需要一塊一塊的開辟空間,同時(shí),你又要注意,它存在的主要意義或者說使用場(chǎng)景...

    rollback 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<