linked list basics
linked list problems
我覺得這兩篇講linked list的文章寫的非常非常好,所以在部落格裡自己寫一下,也算是溫習鞏固資料結構的知識了
本文結合乙個實際的例子來解釋什麼是linked list,如何使用linked list
不過在解釋linked list之前,必須先要了解指標的相關知識,這裡先做乙個簡單回顧一下指標
1.指標儲存了對另乙個變數的引用。 指標的值是乙個位址。
2.在c語言中, * 這個符號被稱為解析符號,它用來取得指標所指向的那個變數的值,舉個例子
#includevoid main ()
上面這個例子在printf列印函式中,*p就是把指標p指向的變數a的值取出來
我記得我在學習c語言指標的時候,一直搞不清楚 * 這個符號,乙個原因就是 int* p=&a 在這行**中, int* p是申明了乙個指標,這一行**也能夠拆成兩行來寫
int *p;
p=&a;
但是請注意,int* p和 int *p這兩種寫法是一樣的,都是申明了乙個指向整形變數的指標,但是 int* p更好,因為 int* 表示這是乙個指向整形的指標,名字叫做p,而 int *p 的寫法容易讓人誤解為指標的名字叫做 *p,我當時學c語言時在這個問題上糾結了很久
3. & 這個符號叫做取位址符,在上面的例子中, int *p = &a; 這行**就是把變數 a 的位址賦給指標 p
關於指標內容其實非常多,這裡只是乙個簡單的回顧,想看更多關於指標的資料,我推薦大家看這篇文章,pointer and memory, 這篇文章是stanford大學的教授寫的,我看完以後,有一種豁然開朗的感覺
1. 陣列的長度是固定的。比如申明了乙個長度為100的整形陣列 int a[100], 陣列a的長度就是100,如果這時需要儲存200個數字,那麼a就用不了了
2. 陣列造成儲存空間的浪費。如果這時陣列a只儲存了 1個整數,那麼它的其他99個位置就相當於浪費了
3. 往陣列裡插入元素的開銷是非常大的。比如要在陣列的第乙個元素的位置上插入1個整數,那就得把陣列其他位置上的元素都向後移動一位
正因為陣列有以上的這些缺點,所以產生了linked list,linked list很好的克服了以上的三個缺點
首先,定義乙個名字叫做node的結構體
struct node ;
這個結構體中包含乙個整數,和乙個指向結構體node的指標
接下來我們寫乙個函式,生成乙個linked list
struct node* buildonetwothreefourfive()
現在,這個linked list就會有5個元素,如果列印出這個linked list的data值,那就是
求linked list長度的函式
int length(struct node* head)
return count;
}
往這個linked list裡新增乙個節點,這個節點要在linked list的頭部,
void push(struct node** headref, int data)
這個push函式是乙個比較難理解的地方,因為它的引數列表有乙個引數 struct node** headref。
我們都知道, struct node* head表示的是乙個指向struct node的指標,名字是head
那麼,struct node** headref表示的就是乙個指向struct node的指標的指標,名字是headref
寫乙個函式,函式有兩個引數,乙個是linked list,另乙個是乙個整數,查詢在這個linked list裡有幾個元素的data值和整個整數相等
int length(struct node* head)
return count;
}
取得linked list中的第n個元素的data值
int getnth(struct node* head, int index)
assert(0);
}
刪除這個linked list
void deletelist(struct node** headref)
*headref = null;
}
取出這個linked list裡頭部的那個節點的data值,並且銷毀第乙個節點
int pop(struct node** headref)
在第n個節點的位置上插入乙個節點
void insertnth(struct node** headref, int index, int data)
assert(current != null);
push(&(current->next),data);
}}
假設乙個linked list已經按照data的公升序排列好了,這時往這個linked list新增乙個節點,這個節點會被加入的正確的位置上
void sortedinsert(struct node** headref, struct node* newnode)
else
newnode->next = current->next;
current->next = newnode;
}}
linked list插入排序
void insertsort(struct node** headref)
*headref = result;
}
把乙個鍊錶加入到另乙個鍊錶的尾部
struct node* current;
if(*aref == null)
else
*bref = null;
}把乙個鍊錶對半分,比如,鍊錶是, 分割後產生兩個鍊錶,第乙個, 第二個
void frontbacksplit(struct node* source, struct node** frontref, struct node** backref)
else
}
去除鍊錶中的重複節點
void removeduplicates(struct node* head)
else
}}
把第二個鍊錶的第乙個節點新增到第乙個鍊錶的頭節點上
void movenode(struct node** destref, struct node** sourceref)
alternatingsplit
每隔乙個節點取出的合成乙個linked list,比如,在 alternatingsplit()後生成兩個鍊錶 和
void alternatingsplit(struct node* source, struct node** aref, struct node** bref)
*aref = a;
*bref = b;
}
shufflemerge
這個函式的目的需要舉例說明,比如,兩個鍊錶和,兩個鍊錶shufflemerge後的結構就是
也就是分別取出每個從每個煉表頭節點合併成乙個鍊錶
struct node* shufflemerge(struct node* a, struct node* b)
}
sortedmerge
假設兩個鍊錶都是按照公升序遞增的,把兩個鍊錶合併,並且合併後的鍊錶也是按照公升序排列的
struct node* sortedmerge(struct node* a, struct node* b)
else if (b == null)
if(a->data <= b->data)
else
tail = tail->next;
} return dummy.next;
}
mergesort
鍊錶的歸併排序
void mergesort(struct node** headref)
sortedintersect
假設兩個鍊錶是按照公升序排列的,找出兩個鍊錶相等的節點
struct node* sortedintersect(struct node* a, struct node* b)
else if(a->data < b->data)
else
} return dummy.next;
}
reverse
鍊錶的倒置
這個是非遞迴版本
void reverse(struct node** headref)
*headref = result;
}
下面這個是遞迴版本
void recursivereverse(struct node** headref)
倒置的操作比較複雜,需要畫圖去跟指標的變化
當然,我覺得最好的方法還是用gdb單步除錯,跟蹤指標變化
LinkedList 鍊錶
線性表是一種簡單的資料結構,其主要特點是元素之間存在 一對一 的關係,除去第乙個元素,每個元素都存在唯一乙個 前驅節點 除去最後乙個元素都存在唯一乙個 後繼節點 簡單的線性表有 陣列 單鏈表 雙向鍊錶 靜態鍊錶等。順序表 陣列 優缺點 陣列不僅邏輯上,物理上位置也相鄰,可隨機訪問,但刪除或插入元素時...
LinkedList 鍊錶
最近複習到鍊錶 linkedlist 一般來說共有大概有兩種實現方式 1.陣列實現 和 2.鏈式實現。我僅使用了直接鏈式實現,如下。其他的實現方式,大家不妨自己嘗試下吧。author ace yom peizhen zhang date 2015 8 17 description 鍊錶實現 ifnd...
鍊錶 LinkedList
原文中singlelinklist的remove方法有問題,因為是 node current firstnode.getnext 所以導致鍊錶的第乙個節點刪不掉。修改如下 public class singlelinklist 刪除某個節點 param element return 刪除成功返回tr...