获取用户列表为空_数据结构和算法(Golang实现)(15)常见数据结构-列表
列表
一、列表 List
我們又經(jīng)常聽到 列表 List 數(shù)據(jù)結(jié)構(gòu),其實這只是更宏觀的統(tǒng)稱,表示存放數(shù)據(jù)的隊列。
列表 List:存放數(shù)據(jù),數(shù)據(jù)按順序排列,可以依次入隊和出隊,有序號關(guān)系,可以取出某序號的數(shù)據(jù)。先進先出的 隊列 (queue) 和先進后出的 棧(stack) 都是列表。大家也經(jīng)常聽說一種叫 線性表 的數(shù)據(jù)結(jié)構(gòu),表示具有相同特性的數(shù)據(jù)元素的有限序列,實際上就是 列表 的同義詞。我們一般寫算法進行數(shù)據(jù)計算,數(shù)據(jù)處理,都需要有個地方來存數(shù)據(jù),我們可以使用封裝好的數(shù)據(jù)結(jié)構(gòu) List:
列表的實現(xiàn)有 順序表示 或 鏈式表示。
順序表示:指的是用一組 地址連續(xù)的存儲單元 依次存儲線性表的數(shù)據(jù)元素,稱為線性表的 順序存儲結(jié)構(gòu)。它以 物理位置相鄰 來表示線性表中數(shù)據(jù)元素間的邏輯關(guān)系,可隨機存取表中任一元素。順序表示的又叫 順序表,也就是用數(shù)組來實現(xiàn)的列表。
鏈式表示:指的是用一組 任意的存儲單元 存儲線性表中的數(shù)據(jù)元素,稱為線性表的 鏈式存儲結(jié)構(gòu)。它的存儲單元可以是連續(xù)的,也可以是不連續(xù)的。在表示數(shù)據(jù)元素之間的邏輯關(guān)系時,除了存儲其本身的信息之外,還需存儲一個指示其直接后繼的信息,也就是用鏈表來實現(xiàn)的列表。
我們在前面已經(jīng)實現(xiàn)過這兩種表示的數(shù)據(jù)結(jié)構(gòu):先進先出的 隊列 (queue) 和先進后出的 棧(stack)。接下來我們會來實現(xiàn)鏈表形式的雙端列表,也叫雙端隊列,這個數(shù)據(jù)結(jié)構(gòu)應(yīng)用場景更廣泛一點。在實際工程應(yīng)用上,緩存數(shù)據(jù)庫 Redis 的 列表List 基本類型就是用它來實現(xiàn)的。
二、實現(xiàn)雙端列表
雙端列表,也可以叫雙端隊列
我們會用雙向鏈表來實現(xiàn)這個數(shù)據(jù)結(jié)構(gòu):
// 雙端列表,雙端隊列 type DoubleList struct {head *ListNode // 指向鏈表頭部tail *ListNode // 指向鏈表尾部len int // 列表長度lock sync.Mutex // 為了進行并發(fā)安全pop操作 }// 列表節(jié)點 type ListNode struct {pre *ListNode // 前驅(qū)節(jié)點next *ListNode // 后驅(qū)節(jié)點value string // 值 }設(shè)計結(jié)構(gòu)體 DoubleList 指向隊列頭部 head 和尾部 tail 的指針字段,方便找到鏈表最前和最后的節(jié)點,并且鏈表節(jié)點之間是雙向鏈接的,鏈表的第一個元素的前驅(qū)節(jié)點為 nil,最后一個元素的后驅(qū)節(jié)點也為 nil。如圖:
我們實現(xiàn)的雙端列表和 Golang 標準庫 container/list 中實現(xiàn)的不一樣,感興趣的可以閱讀標準庫的實現(xiàn)。
2.1.列表節(jié)點普通操作
// 獲取節(jié)點值 func (node *ListNode) GetValue() string {return node.value }// 獲取節(jié)點前驅(qū)節(jié)點 func (node *ListNode) GetPre() *ListNode {return node.pre }// 獲取節(jié)點后驅(qū)節(jié)點 func (node *ListNode) GetNext() *ListNode {return node.next }// 是否存在后驅(qū)節(jié)點 func (node *ListNode) HashNext() bool {return node.pre != nil }// 是否存在前驅(qū)節(jié)點 func (node *ListNode) HashPre() bool {return node.next != nil }// 是否為空節(jié)點 func (node *ListNode) IsNil() bool {return node == nil }以上是對節(jié)點結(jié)構(gòu)體 ListNode 的操作,主要判斷節(jié)點是否為空,有沒有后驅(qū)和前驅(qū)節(jié)點,返回值等,時間復雜度都是 O(1)。
2.2.從頭部開始某個位置前插入新節(jié)點
// 添加節(jié)點到鏈表頭部的第N個元素之前,N=0表示新節(jié)點成為新的頭部 func (list *DoubleList) AddNodeFromHead(n int, v string) {// 加并發(fā)鎖list.lock.Lock()defer list.lock.Unlock()// 索引超過列表長度,一定找不到,panicif n > list.len {panic("index out")}// 先找出頭部node := list.head// 往后遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.next}// 新節(jié)點newNode := new(ListNode)newNode.value = v// 如果定位到的節(jié)點為空,表示列表為空,將新節(jié)點設(shè)置為新頭部和新尾部if node.IsNil() {list.head = newNodelist.tail = newNode} else {// 定位到的節(jié)點,它的前驅(qū)pre := node.pre// 如果定位到的節(jié)點前驅(qū)為nil,那么定位到的節(jié)點為鏈表頭部,需要換頭部if pre.IsNil() {// 將新節(jié)點鏈接在老頭部之前newNode.next = nodenode.pre = newNode// 新節(jié)點成為頭部list.head = newNode} else {// 將新節(jié)點插入到定位到的節(jié)點之前// 定位到的節(jié)點的前驅(qū)節(jié)點 pre 現(xiàn)在鏈接到新節(jié)點上pre.next = newNodenewNode.pre = pre// 定位到的節(jié)點的后驅(qū)節(jié)點 node.next 現(xiàn)在鏈接到新節(jié)點上node.next.pre = newNodenewNode.next = node.next}}// 列表長度+1list.len = list.len + 1 }首先加鎖實現(xiàn)并發(fā)安全。然后判斷索引是否超出列表長度:
// 索引超過列表長度,一定找不到,panicif n > list.len {panic("index out")}如果 n=0 表示新節(jié)點想成為新的鏈表頭部,n=1 表示插入到鏈表頭部數(shù)起第二個節(jié)點之前,新節(jié)點成為第二個節(jié)點,以此類推。
首先,找出頭部:node := list.head,然后往后面遍歷,定位到索引指定的節(jié)點 node:
// 往后遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.next}接著初始化新節(jié)點:newNode := new(ListNode)。
定位到的節(jié)點有三種情況,我們需要在該節(jié)點之前插入新節(jié)點:
判斷定位到的節(jié)點 node 是否為空,如果為空,表明列表沒有元素,將新節(jié)點設(shè)置為新頭部和新尾部。
否則找到定位到的節(jié)點的前驅(qū)節(jié)點:pre := node.pre。
如果前驅(qū)節(jié)點為空:pre.IsNil(),表明定位到的節(jié)點 node 為頭部,那么新節(jié)點要取代它,成為新的頭部:
if pre.IsNil() {// 將新節(jié)點鏈接在老頭部之前newNode.next = nodenode.pre = newNode// 新節(jié)點成為頭部list.head = newNode}新節(jié)點成為新的頭部,需要將新節(jié)點的后驅(qū)設(shè)置為老頭部:newNode.next = node,老頭部的前驅(qū)為新頭部:node.pre = newNode,并且新頭部變化:list.head = newNode。
如果定位到的節(jié)點的前驅(qū)節(jié)點不為空,表明定位到的節(jié)點 node 不是頭部節(jié)點,那么我們只需將新節(jié)點鏈接到節(jié)點 node 之前即可:
// 定位到的節(jié)點的前驅(qū)節(jié)點 pre 現(xiàn)在鏈接到新節(jié)點前pre.next = newNodenewNode.pre = pre// 定位到的節(jié)點鏈接到新節(jié)點之后newNode.next = nodenode.pre = newNode先將定位到的節(jié)點的前驅(qū)節(jié)點和新節(jié)點綁定,因為現(xiàn)在新節(jié)點插在前面了,把定位節(jié)點的前驅(qū)節(jié)點的后驅(qū)設(shè)置為新節(jié)點:pre.next = newNode,新節(jié)點的前驅(qū)設(shè)置為定位節(jié)點的前驅(qū)節(jié)點:newNode.pre = pre。
同時,定位到的節(jié)點現(xiàn)在要鏈接到新節(jié)點之后,所以新節(jié)點的后驅(qū)設(shè)置為:newNode.next = node,定位到的節(jié)點的前驅(qū)設(shè)置為:node.pre = newNode。
最后,鏈表長度加一。
大部分時間花在遍歷位置上,如果 n=0,那么時間復雜度為 O(1),否則為 O(n)。
2.3.從尾部開始某個位置后插入新節(jié)點
// 添加節(jié)點到鏈表尾部的第N個元素之后,N=0表示新節(jié)點成為新的尾部 func (list *DoubleList) AddNodeFromTail(n int, v string) {// 加并發(fā)鎖list.lock.Lock()defer list.lock.Unlock()// 索引超過列表長度,一定找不到,panicif n > list.len {panic("index out")}// 先找出尾部node := list.tail// 往前遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.pre}// 新節(jié)點newNode := new(ListNode)newNode.value = v// 如果定位到的節(jié)點為空,表示列表為空,將新節(jié)點設(shè)置為新頭部和新尾部if node.IsNil() {list.head = newNodelist.tail = newNode} else {// 定位到的節(jié)點,它的后驅(qū)next := node.next// 如果定位到的節(jié)點后驅(qū)為nil,那么定位到的節(jié)點為鏈表尾部,需要換尾部if next.IsNil() {// 將新節(jié)點鏈接在老尾部之后node.next = newNodenewNode.pre = node// 新節(jié)點成為尾部list.tail = newNode} else {// 將新節(jié)點插入到定位到的節(jié)點之后// 新節(jié)點鏈接到定位到的節(jié)點之后newNode.pre = nodenode.next = newNode// 定位到的節(jié)點的后驅(qū)節(jié)點鏈接在新節(jié)點之后newNode.next = nextnext.pre = newNode}}// 列表長度+1list.len = list.len + 1 }操作和頭部插入節(jié)點相似,自行分析。
2.4.從頭部開始某個位置獲取列表節(jié)點
// 從頭部開始往后找,獲取第N+1個位置的節(jié)點,索引從0開始。 func (list *DoubleList) IndexFromHead(n int) *ListNode {// 索引超過或等于列表長度,一定找不到,返回空指針if n >= list.len {return nil}// 獲取頭部節(jié)點node := list.head// 往后遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.next}return node }如果索引超出或等于列表長度,那么找不到節(jié)點,返回空。
否則從頭部開始遍歷,拿到節(jié)點。
時間復雜度為:O(n)。
2.5.從尾部開始某個位置獲取列表節(jié)點
// 從尾部開始往前找,獲取第N+1個位置的節(jié)點,索引從0開始。 func (list *DoubleList) IndexFromTail(n int) *ListNode {// 索引超過或等于列表長度,一定找不到,返回空指針if n >= list.len {return nil}// 獲取尾部節(jié)點node := list.tail// 往前遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.pre}return node }操作和從頭部獲取節(jié)點一樣,請自行分析。
2.6.從頭部開始移除并返回某個位置的節(jié)點
// 從頭部開始往后找,獲取第N+1個位置的節(jié)點,并移除返回 func (list *DoubleList) PopFromHead(n int) *ListNode {// 加并發(fā)鎖list.lock.Lock()defer list.lock.Unlock()// 索引超過或等于列表長度,一定找不到,返回空指針if n >= list.len {return nil}// 獲取頭部node := list.head// 往后遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.next}// 移除的節(jié)點的前驅(qū)和后驅(qū)pre := node.prenext := node.next// 如果前驅(qū)和后驅(qū)都為nil,那么移除的節(jié)點為鏈表唯一節(jié)點if pre.IsNil() && next.IsNil() {list.head = nillist.tail = nil} else if pre.IsNil() {// 表示移除的是頭部節(jié)點,那么下一個節(jié)點成為頭節(jié)點list.head = nextnext.pre = nil} else if next.IsNil() {// 表示移除的是尾部節(jié)點,那么上一個節(jié)點成為尾節(jié)點list.tail = prepre.next = nil} else {// 移除的是中間節(jié)點pre.next = nextnext.pre = pre}// 節(jié)點減一list.len = list.len - 1return node }首先加并發(fā)鎖實現(xiàn)并發(fā)安全。先判斷索引是否超出列表長度:n >= list.len,如果超出直接返回空指針。
獲取頭部,然后遍歷定位到第 N+1 個位置的元素:node = node.next。
定位到的并要移除的節(jié)點有三種情況發(fā)生:
查看要移除的節(jié)點的前驅(qū)和后驅(qū):
// 移除的節(jié)點的前驅(qū)和后驅(qū)pre := node.prenext := node.next如果前驅(qū)和后驅(qū)都為空:pre.IsNil() && next.IsNil(),那么要移除的節(jié)點是鏈表中唯一的節(jié)點,直接將列表頭部和尾部置空即可。
如果前驅(qū)節(jié)點為空:pre.IsNil(),表示移除的是頭部節(jié)點,那么頭部節(jié)點的下一個節(jié)點要成為新的頭部:list.head = next,并且這時新的頭部前驅(qū)要設(shè)置為空:next.pre = nil。
同理,如果后驅(qū)節(jié)點為空:next.IsNil(),表示移除的是尾部節(jié)點,需要將尾部節(jié)點的前一個節(jié)點設(shè)置為新的尾部:list.tail = pre,并且這時新的尾部后驅(qū)要設(shè)置為空:pre.next = nil。
如果移除的節(jié)點處于兩個節(jié)點之間,那么將這兩個節(jié)點鏈接起來即可:
// 移除的是中間節(jié)點pre.next = nextnext.pre = pre最后,列表長度減一。
主要的耗時用在定位節(jié)點上,其他的操作都是鏈表鏈接,可以知道時間復雜度為:O(n)。
2.7.從尾部開始移除并返回某個位置的節(jié)點
// 從尾部開始往前找,獲取第N+1個位置的節(jié)點,并移除返回 func (list *DoubleList) PopFromTail(n int) *ListNode {// 加并發(fā)鎖list.lock.Lock()defer list.lock.Unlock()// 索引超過或等于列表長度,一定找不到,返回空指針if n >= list.len {return nil}// 獲取尾部node := list.tail// 往前遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.pre}// 移除的節(jié)點的前驅(qū)和后驅(qū)pre := node.prenext := node.next// 如果前驅(qū)和后驅(qū)都為nil,那么移除的節(jié)點為鏈表唯一節(jié)點if pre.IsNil() && next.IsNil() {list.head = nillist.tail = nil} else if pre.IsNil() {// 表示移除的是頭部節(jié)點,那么下一個節(jié)點成為頭節(jié)點list.head = nextnext.pre = nil} else if next.IsNil() {// 表示移除的是尾部節(jié)點,那么上一個節(jié)點成為尾節(jié)點list.tail = prepre.next = nil} else {// 移除的是中間節(jié)點pre.next = nextnext.pre = pre}// 節(jié)點減一list.len = list.len - 1return node }操作和從頭部移除節(jié)點相似,請自行分析。
2.8.完整例子
package mainimport ("fmt""sync" )// 雙端列表,雙端隊列 type DoubleList struct {head *ListNode // 指向鏈表頭部tail *ListNode // 指向鏈表尾部len int // 列表長度lock sync.Mutex // 為了進行并發(fā)安全pop操作 }// 列表節(jié)點 type ListNode struct {pre *ListNode // 前驅(qū)節(jié)點next *ListNode // 后驅(qū)節(jié)點value string // 值 }// 獲取節(jié)點值 func (node *ListNode) GetValue() string {return node.value }// 獲取節(jié)點前驅(qū)節(jié)點 func (node *ListNode) GetPre() *ListNode {return node.pre }// 獲取節(jié)點后驅(qū)節(jié)點 func (node *ListNode) GetNext() *ListNode {return node.next }// 是否存在后驅(qū)節(jié)點 func (node *ListNode) HashNext() bool {return node.pre != nil }// 是否存在前驅(qū)節(jié)點 func (node *ListNode) HashPre() bool {return node.next != nil }// 是否為空節(jié)點 func (node *ListNode) IsNil() bool {return node == nil }// 返回列表長度 func (list *DoubleList) Len() int {return list.len }// 添加節(jié)點到鏈表頭部的第N個元素之前,N=0表示新節(jié)點成為新的頭部 func (list *DoubleList) AddNodeFromHead(n int, v string) {// 加并發(fā)鎖list.lock.Lock()defer list.lock.Unlock()// 索引超過列表長度,一定找不到,panicif n > list.len {panic("index out")}// 先找出頭部node := list.head// 往后遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.next}// 新節(jié)點newNode := new(ListNode)newNode.value = v// 如果定位到的節(jié)點為空,表示列表為空,將新節(jié)點設(shè)置為新頭部和新尾部if node.IsNil() {list.head = newNodelist.tail = newNode} else {// 定位到的節(jié)點,它的前驅(qū)pre := node.pre// 如果定位到的節(jié)點前驅(qū)為nil,那么定位到的節(jié)點為鏈表頭部,需要換頭部if pre.IsNil() {// 將新節(jié)點鏈接在老頭部之前newNode.next = nodenode.pre = newNode// 新節(jié)點成為頭部list.head = newNode} else {// 將新節(jié)點插入到定位到的節(jié)點之前// 定位到的節(jié)點的前驅(qū)節(jié)點 pre 現(xiàn)在鏈接到新節(jié)點前pre.next = newNodenewNode.pre = pre// 定位到的節(jié)點鏈接到新節(jié)點之后newNode.next = nodenode.pre = newNode}}// 列表長度+1list.len = list.len + 1 }// 添加節(jié)點到鏈表尾部的第N個元素之后,N=0表示新節(jié)點成為新的尾部 func (list *DoubleList) AddNodeFromTail(n int, v string) {// 加并發(fā)鎖list.lock.Lock()defer list.lock.Unlock()// 索引超過列表長度,一定找不到,panicif n > list.len {panic("index out")}// 先找出尾部node := list.tail// 往前遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.pre}// 新節(jié)點newNode := new(ListNode)newNode.value = v// 如果定位到的節(jié)點為空,表示列表為空,將新節(jié)點設(shè)置為新頭部和新尾部if node.IsNil() {list.head = newNodelist.tail = newNode} else {// 定位到的節(jié)點,它的后驅(qū)next := node.next// 如果定位到的節(jié)點后驅(qū)為nil,那么定位到的節(jié)點為鏈表尾部,需要換尾部if next.IsNil() {// 將新節(jié)點鏈接在老尾部之后node.next = newNodenewNode.pre = node// 新節(jié)點成為尾部list.tail = newNode} else {// 將新節(jié)點插入到定位到的節(jié)點之后// 新節(jié)點鏈接到定位到的節(jié)點之后newNode.pre = nodenode.next = newNode// 定位到的節(jié)點的后驅(qū)節(jié)點鏈接在新節(jié)點之后newNode.next = nextnext.pre = newNode}}// 列表長度+1list.len = list.len + 1 }// 返回列表鏈表頭結(jié)點 func (list *DoubleList) First() *ListNode {return list.head }// 返回列表鏈表尾結(jié)點 func (list *DoubleList) Last() *ListNode {return list.tail }// 從頭部開始往后找,獲取第N+1個位置的節(jié)點,索引從0開始。 func (list *DoubleList) IndexFromHead(n int) *ListNode {// 索引超過或等于列表長度,一定找不到,返回空指針if n >= list.len {return nil}// 獲取頭部節(jié)點node := list.head// 往后遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.next}return node }// 從尾部開始往前找,獲取第N+1個位置的節(jié)點,索引從0開始。 func (list *DoubleList) IndexFromTail(n int) *ListNode {// 索引超過或等于列表長度,一定找不到,返回空指針if n >= list.len {return nil}// 獲取尾部節(jié)點node := list.tail// 往前遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.pre}return node }// 從頭部開始往后找,獲取第N+1個位置的節(jié)點,并移除返回 func (list *DoubleList) PopFromHead(n int) *ListNode {// 加并發(fā)鎖list.lock.Lock()defer list.lock.Unlock()// 索引超過或等于列表長度,一定找不到,返回空指針if n >= list.len {return nil}// 獲取頭部node := list.head// 往后遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.next}// 移除的節(jié)點的前驅(qū)和后驅(qū)pre := node.prenext := node.next// 如果前驅(qū)和后驅(qū)都為nil,那么移除的節(jié)點為鏈表唯一節(jié)點if pre.IsNil() && next.IsNil() {list.head = nillist.tail = nil} else if pre.IsNil() {// 表示移除的是頭部節(jié)點,那么下一個節(jié)點成為頭節(jié)點list.head = nextnext.pre = nil} else if next.IsNil() {// 表示移除的是尾部節(jié)點,那么上一個節(jié)點成為尾節(jié)點list.tail = prepre.next = nil} else {// 移除的是中間節(jié)點pre.next = nextnext.pre = pre}// 節(jié)點減一list.len = list.len - 1return node }// 從尾部開始往前找,獲取第N+1個位置的節(jié)點,并移除返回 func (list *DoubleList) PopFromTail(n int) *ListNode {// 加并發(fā)鎖list.lock.Lock()defer list.lock.Unlock()// 索引超過或等于列表長度,一定找不到,返回空指針if n >= list.len {return nil}// 獲取尾部node := list.tail// 往前遍歷拿到第 N+1 個位置的元素for i := 1; i <= n; i++ {node = node.pre}// 移除的節(jié)點的前驅(qū)和后驅(qū)pre := node.prenext := node.next// 如果前驅(qū)和后驅(qū)都為nil,那么移除的節(jié)點為鏈表唯一節(jié)點if pre.IsNil() && next.IsNil() {list.head = nillist.tail = nil} else if pre.IsNil() {// 表示移除的是頭部節(jié)點,那么下一個節(jié)點成為頭節(jié)點list.head = nextnext.pre = nil} else if next.IsNil() {// 表示移除的是尾部節(jié)點,那么上一個節(jié)點成為尾節(jié)點list.tail = prepre.next = nil} else {// 移除的是中間節(jié)點pre.next = nextnext.pre = pre}// 節(jié)點減一list.len = list.len - 1return node }func main() {list := new(DoubleList)// 在列表頭部插入新元素list.AddNodeFromHead(0, "I")list.AddNodeFromHead(0, "love")list.AddNodeFromHead(0, "you")// 在列表尾部插入新元素list.AddNodeFromTail(0, "may")list.AddNodeFromTail(0, "happy")// 正常遍歷,比較慢for i := 0; i < list.Len(); i++ {// 從頭部開始索引node := list.IndexFromHead(i)// 節(jié)點為空不可能,因為list.Len()使得索引不會越界if !node.IsNil() {fmt.Println(node.GetValue())}}fmt.Println("----------")// 正常遍歷,特別快// 先取出第一個元素first := list.First()for !first.IsNil() {// 如果非空就一直遍歷fmt.Println(first.GetValue())// 接著下一個節(jié)點first = first.GetNext()}fmt.Println("----------")// 元素一個個 POP 出來for {node := list.PopFromHead(0)if node.IsNil() {// 沒有元素了,直接返回break}fmt.Println(node.GetValue())}fmt.Println("----------")fmt.Println("len", list.Len()) }輸出:
you love I may happy ---------- you love I may happy ---------- you love I may happy ---------- len 0首先,先從列表頭部插入三個新元素,然后從尾部插入兩個新元素,然后用三種方式進行遍歷,兩種只是查看元素,一種是遍歷移除元素。
系列文章入口
我是陳星星,歡迎閱讀我親自寫的 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn)),文章首發(fā)于
目錄 · 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))?goa.lenggirl.com- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(1)簡單入門Golang-前言
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(2)簡單入門Golang-包、變量和函數(shù)
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(3)簡單入門Golang-流程控制語句
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(4)簡單入門Golang-結(jié)構(gòu)體和方法
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(5)簡單入門Golang-接口
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(6)簡單入門Golang-并發(fā)、協(xié)程和信道
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(7)簡單入門Golang-標準庫
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(8.1)基礎(chǔ)知識-前言
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(8.2)基礎(chǔ)知識-分治法和遞歸
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(9)基礎(chǔ)知識-算法復雜度及漸進符號
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(10)基礎(chǔ)知識-算法復雜度主方法
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(11)常見數(shù)據(jù)結(jié)構(gòu)-前言
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(12)常見數(shù)據(jù)結(jié)構(gòu)-鏈表
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(13)常見數(shù)據(jù)結(jié)構(gòu)-可變長數(shù)組
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(14)常見數(shù)據(jù)結(jié)構(gòu)-棧和隊列
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(15)常見數(shù)據(jù)結(jié)構(gòu)-列表
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(16)常見數(shù)據(jù)結(jié)構(gòu)-字典
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(17)常見數(shù)據(jù)結(jié)構(gòu)-樹
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(18)排序算法-前言
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(19)排序算法-冒泡排序
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(20)排序算法-選擇排序
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(21)排序算法-插入排序
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(22)排序算法-希爾排序
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(23)排序算法-歸并排序
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(24)排序算法-優(yōu)先隊列及堆排序
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(25)排序算法-快速排序
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(26)查找算法-哈希表
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(27)查找算法-二叉查找樹
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(28)查找算法-AVL樹
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(29)查找算法-2-3樹和左傾紅黑樹
- 數(shù)據(jù)結(jié)構(gòu)和算法(Golang實現(xiàn))(30)查找算法-2-3-4樹和普通紅黑樹
總結(jié)
以上是生活随笔為你收集整理的获取用户列表为空_数据结构和算法(Golang实现)(15)常见数据结构-列表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 挑战握手协议_什么是挑战握手
- 下一篇: java过滤器经典案例_JAVA语言基础