數(shù)據(jù)結(jié)構(gòu)之線性表

其他文章:
數(shù)據(jù)結(jié)構(gòu)之棧和隊(duì)列

文章目錄
1.1 概念
1.2 順序表
1.3 鏈表
1.4 兩種存儲(chǔ)方式的比較
1.5 兩種表的實(shí)現(xiàn)方式及其各種操作
1.5.1 順序表
1.5.2 單鏈表
1.6 應(yīng)用實(shí)例

1.1 概念

線性表是具有相同特性的數(shù)據(jù)元素的一個(gè)有限序列。

注:注意概念中的關(guān)鍵字政冻,線性表中的每一項(xiàng)叫數(shù)據(jù)元素,它是有限的。另外值得注意的是涯保,線性表是可以是空表仪召,即它的長(zhǎng)度是0時(shí)為空表印屁。

數(shù)據(jù)結(jié)構(gòu)中,線性表分為順序表和鏈表留量。

下面介紹兩種存儲(chǔ)形式的概念的特性以及兩者之間的比較。

1.2 順序表

順序表就是把線性表中的所有元素按照其邏輯順序哟冬,依次存儲(chǔ)到從指定的存
儲(chǔ)位置開始的一塊連續(xù)的存儲(chǔ)空間中楼熄。

注:說(shuō)白了,在C語(yǔ)言中浩峡,就是數(shù)組可岂,而數(shù)組的特點(diǎn)就是一塊連續(xù)的存儲(chǔ)空間。所謂的邏輯順序翰灾,按照我的理解缕粹,就是其排列的順序稚茅。或從小到大或者從大到小或者亂序致开,或者按照某個(gè)特定的規(guī)則排序峰锁。(不知道有沒有理解到位!K痢:缃!)

1.3 鏈表

在鏈表存儲(chǔ)中飒货,每個(gè)節(jié)點(diǎn)不僅包含所有元素本身的信息魄衅,還包含元素之間邏輯關(guān)系的信息,即前驅(qū)節(jié)點(diǎn)包含后繼節(jié)點(diǎn)的地址信息塘辅。

1.4 兩種存儲(chǔ)方式的比較:

1晃虫、順序表的特點(diǎn):

1)隨機(jī)訪問特性。順序表在查找方面效率較高扣墩,只要知道下標(biāo)哲银,立馬就能獲取到該元素。
2)順序表要求占用連續(xù)的存儲(chǔ)空間呻惕。這是順序表的存儲(chǔ)結(jié)構(gòu)決定的荆责,它用數(shù)組表示,就必須是占用連續(xù)的存儲(chǔ)空間亚脆。
3)存儲(chǔ)分配只能預(yù)先進(jìn)行做院,即靜態(tài)分配。就像C語(yǔ)言中數(shù)組的聲明一樣濒持,需要在聲明的時(shí)候就需要確定其長(zhǎng)度键耕,例如int a[30];長(zhǎng)度為30的int型數(shù)組。

2柑营、鏈表的特點(diǎn)

1)不支持隨機(jī)訪問屈雄。因?yàn)殒湵碇忻總€(gè)元素的地址保存在其前驅(qū)節(jié)點(diǎn)中的指針域,所以要訪問該節(jié)點(diǎn)官套,必須知道其前驅(qū)節(jié)點(diǎn)棚亩,所以訪問節(jié)點(diǎn)每次都是從頭結(jié)點(diǎn)開始。
2)節(jié)點(diǎn)的存儲(chǔ)空間利用率較順序表稍低一些虏杰。
3)動(dòng)態(tài)分配存儲(chǔ)空間讥蟆。鏈表可以任意增加鏈表長(zhǎng)度和縮短鏈表長(zhǎng)度,這得益于它的結(jié)構(gòu)特點(diǎn):鏈?zhǔn)健?/p>

注:順序表在做插入操作時(shí)需要移動(dòng)多個(gè)元素纺阔,而鏈表則不需要移動(dòng)任何元素瘸彤。

1.5 兩種表的實(shí)現(xiàn)方式及其各種操作

首先定義幾個(gè)常量,代碼如下(C/C++):

#define MAXSIZE 100     //線性表的最大長(zhǎng)度
#define DataType int    //數(shù)據(jù)的類型

1.5.1 順序表

線性表的定義只有兩個(gè)數(shù)據(jù)域笛钝,一個(gè)是存儲(chǔ)數(shù)據(jù)信息的數(shù)組质况,一個(gè)是保存線性表長(zhǎng)度的length變量愕宋。代碼如下:

typedef struct {
    DataType data[MAXSIZE];    //數(shù)據(jù)域
    int length;                //順序表長(zhǎng)度
}SqList;

以下是順序表的各種操作

1.初始化操作

順序表的初始化操作只需要將length初始化為0即可.代碼如下:

void Init(SqList &L){
    L.length = 0;
}

2、插入操作

插入操作可分為以下幾步(順序表的存儲(chǔ)開始位置為1):
(1) 判斷插入位置index的合法性以及l(fā)ength的合法性(也可以增加順序表的最大長(zhǎng)度).
(2)將第index個(gè)元素的后續(xù)元素后移一位.
(3)將e值插入index位置,并將length加1.

int Insert(SqList &L, int index, DataType e) {

    //首先判斷index的合法性
    //1 <= index <= length + 1, 元素開始下標(biāo)為1,可以插入順序表尾部
    //L表的長(zhǎng)度也有限制结榄,必須小于定義的長(zhǎng)度 MAXSIZE - 1
    if(index < 1 || index > L.length + 1 || L.length == MAXSIZE - 1) {
        return 0;   //插入失敗返回0
    }
    int i;
    //參數(shù)合法中贝,開始插入操作
    //1、先將需要移動(dòng)的元素移動(dòng)
    for(i = L.length; i >= index; --i) {
        L.data[i + 1] = L.data[i];
    }
    //2臼朗、最后index位置空著邻寿,將e插入
    L.data[index] = e;
    //3、長(zhǎng)度加1
    L.length++;
    return 1;   //插入成功视哑,返回1
}

3.刪除操作

刪除操作可分為以下幾步:
(1) 判斷插入位置index的合法性.
(2)將第index個(gè)元素的后續(xù)元素前移一位.
(3)將e值插入index位置,并將length減1.

int Delete(SqList &L, int index, DataType &e) {

    //index的合法性
    //index的范圍是1 <= index <= L.length
    if(index < 1 || index > L.length) {
        return 0;   //刪除失敗,返回0
    }

    //1.找到要?jiǎng)h除的元素并賦給e
    e = L.data[index];

    //2.將第index元素的后面的元素前移一位
    int i;
    for(i = index + 1; i <= L.length; i++) {
        L.data[i - 1] = L.data[i];
    }

    //3.順序表的長(zhǎng)度減1
    L.length--;
}

4.定位操作

因?yàn)轫樞虮硎菑南聵?biāo)為1的位置開始存儲(chǔ),所以可以作如下判斷,查找元素值為e的順序表中的元素時(shí),若查找失敗返回0,查找成功返回下標(biāo),成功時(shí)下標(biāo)>0.代碼比較簡(jiǎn)單,如下:

int LocateElem(SqList L, DataType e) {

    int i;

    for(i = 1; i <= L.length; i++) {
        if(L.data[i] == e) {
            return i;   //查找成功绣否,返回i
        }
    }
    return 0;   //若查找失敗,返回0
}

5.遍歷操作

遍歷操作很簡(jiǎn)單,代碼如下:

void Traverse(SqList L) {
    for(int i = 1; i <= L.length; i++) {
        printf(" %d", L.data[i]);
    }
}

6.判斷順序表是否為空

順序表是否為空只需要根據(jù)length的值判斷即可,代碼如下:

int IsEmpty(SqList L){

    if(L.length > 0){
        return 1;
    }
    return 0;
}

順序表的實(shí)現(xiàn)算是比較簡(jiǎn)單的.可以加入自動(dòng)增長(zhǎng)順序表空間的功能,只需要判斷是否已經(jīng)滿了,若滿了,只需要另外開辟一個(gè)增長(zhǎng)的空間即可.此時(shí)順序表的結(jié)構(gòu)體需要換成指針式的.具體的代碼,可以見數(shù)據(jù)結(jié)構(gòu)課本.或者下載完整的代碼,我將會(huì)在博客最后貼出.

下面看看鏈表的存儲(chǔ)結(jié)構(gòu)和各種操作實(shí)現(xiàn).

1.5.2 單鏈表

先定義兩個(gè)數(shù)據(jù)類型:

typedef int ElemType;   //數(shù)據(jù)類型
typedef int Status;     //返回值類型

單鏈表的定義也只有兩個(gè)域,即數(shù)據(jù)域和指針域,數(shù)據(jù)域保存當(dāng)前節(jié)點(diǎn)的數(shù)據(jù),可以多種(代碼為整形變量),指針域保存下一個(gè)節(jié)點(diǎn)的地址.具體代碼如下:

typedef struct LNode {
    ElemType data;
    struct LNode *next;
} LNode, *LinkedList;

以下是單鏈表的各種操作

1.初始化操作:

初始化操作是生成一個(gè)頭結(jié)點(diǎn),并將頭結(jié)點(diǎn)的next值賦值為NULL即可,代碼如下:

Status Init(LinkedList &head) {
    head = (LinkedList) malloc(sizeof(LNode));

    if (!head)return ERROR;

    head->next = NULL;
    return OK;
}

2.插入操作:

插入操作要考慮的因素有以下幾點(diǎn):
(1)插入的位置的合法性
(2)單鏈表因?yàn)槠涮攸c(diǎn),插入操作比較簡(jiǎn)單,只需要修改指針即可,所以難點(diǎn)是在插入位置的判斷上.代碼如下:

Status Insert(LinkedList &L, int i, ElemType e) 

    LinkedList p = L;

    LinkedList s;   //待插入節(jié)點(diǎn)

    int j = 0;      //j=0時(shí),p指向頭結(jié)點(diǎn)
    
    //找到第i個(gè)節(jié)點(diǎn)的位置,循環(huán)結(jié)束之后,p指向第i-1個(gè)元素
    while (p && j < i - 1) {
        p = p->next;
        ++j;
    }
    
    //若i小于1,或者i大于鏈表長(zhǎng)度+1
    //i小于1時(shí),循環(huán)之后j > i - 1
    //若i > 長(zhǎng)度+1,那么最后p指向的是NULL,而本應(yīng)該指向的是第i-1個(gè)元素
    if (!p || j > i - 1)return ERROR;

    //為新節(jié)點(diǎn)開辟內(nèi)存空間
    s = (LinkedList) malloc(sizeof(LNode));
    
    //將新元素插入其中
    s->data = e;
    s->next = p->next;
    p->next = s;

    return OK;
}

3.刪除操作

刪除操作原理同上.需要注意的是,刪除元素的i的范圍不一樣.因?yàn)椴荒軇h除第length+1個(gè)元素,因?yàn)椴淮嬖?

Status Delete(LinkedList &L, int i, ElemType &e) {

    LinkedList q, p = L;

    int j = 0;

    while (p && j < i - 1) {//find the  No. i-1 LNode
        p = p->next;
        ++j;
    }
    
    if (!p || j > i - 1)return ERROR;

    //if L has element[i], p->next is now point to it.
    q = p->next;
    p->next = q->next;
    e = q->data;

    free(q);

    return OK;
}

4.獲取元素

獲取元素的原理也同上,需要注意的是,同插入不同.獲取元素的范圍和刪除時(shí)一樣.以下代碼因?yàn)閜和j的初始值不一樣,所以后面的判斷也是不一樣的,需要注意.代碼如下:

Status GetElem(LinkedList &L, int i, ElemType &e) {

    LinkedList p = L->next; //這里開始指向的是第一個(gè)節(jié)點(diǎn)

    int j = 1;//所以j也要相應(yīng)的指向第一個(gè)節(jié)點(diǎn)

    while (p && j < i) {
        p = p->next;
        ++j;
    }

    if (!p || j > i)return ERROR;

    e = p->data;

    return OK;
}

5.一個(gè)重要但是簡(jiǎn)單的操作就是遍歷操作.代碼如下:

Status Traverse(LinkedList &L) {

    int count = 0;
    
    LinkedList p = L->next;

    while (p) {
        count++;
        cout << p->data << " ";
        p = p->next;
    }
}

6.下面介紹兩個(gè)重要的操作,就是尾插法和頭插法建立單鏈表的操作.

頭插法,即在頭部插入元素,就是每個(gè)元素插入之后,會(huì)在鏈表的第一的位置,所以頭插法建立的單鏈表是倒序的.
頭插法關(guān)鍵步驟:
(1)建立新的節(jié)點(diǎn)保存待插入元素.
(2)將此節(jié)點(diǎn)的指針域(next指針指向頭元素,即第一個(gè)元素,即p->next = head->next;).
(3)將頭指針指向p節(jié)點(diǎn)(新建立的節(jié)點(diǎn)),即head->next = p;.

頭插法代碼如下:

//create a length's LinkedList by head insert method
//this method causes a reversed sequence.
void CreateListHead(LinkedList &L, int length) {

    //first, create a null LinkedList with a head node
    L = (LinkedList) malloc(sizeof(LNode));

    L->next = NULL;

    LinkedList p;

    //input the array that will be inserted.
    cout << "Please enter " << length << " numbers." << endl;

    ElemType e[length];

    for (int i = 0; i < length; i++) {
        cin >> e[i];
    }

    //create node and insert into head->next
    for (int i = 0; i < length; i++) {
        p = (LinkedList) malloc(sizeof(LNode));
        p->data = e[i];

        //insert
        p->next = L->next;
        L->next = p;
    }
}

尾插法和頭插法正好相反,是在尾部插入,順序是正的.

尾插法的關(guān)鍵步驟如下:
(1)建立一個(gè)指向末尾元素的指針,r,初始指向頭結(jié)點(diǎn),隨著元素的插入,每次都指向末尾元素.
(2)建立一個(gè)新的元素p,用于保存插入的元素.
(3)對(duì)p賦值之后接入鏈表末尾,即r->next = p;
(4)更新r的位置,即r = p;.

尾插法的代碼如下,需要注意一些細(xì)節(jié):

//create a length's LinkedList by rear insert method
//this method causes a normal order sequence.
void CreateListRear(LinkedList &L, int length) {

    //first, create a null LinkedList with a head node
    L = (LinkedList) malloc(sizeof(LNode));

    //declare a new LinkedList which point to L(null at first)
    //r point to the end of L when inserting.
    LinkedList r = L;

    //input the array supposed to be inserted.
    cout << "Please enter " << length << " numbers." << endl;

    ElemType e[length];

    for (int i = 0; i < length; i++) {
        scanf("%d", &e[i]);
    }

    LinkedList p;

    //create node and insert into head->next
    for (int i = 0; i < length; i++) {
        p = (LinkedList) malloc(sizeof(LNode));

        p->data = e[i];

        //insert
        r->next = p;
        r = p;
    }

    r->next = NULL;//the pointer of the last elem. must be NULL.
}

1.6 應(yīng)用實(shí)例

至此,順序表的單鏈表的存儲(chǔ)結(jié)構(gòu)和基本操作已經(jīng)整理完畢.下面給出兩個(gè)個(gè)例題,作為操練:
(1)請(qǐng)逆序打印出單鏈表的所有元素,假設(shè)初始時(shí),L指針指向單鏈表的開始節(jié)點(diǎn).

解析:開始時(shí)指向第一個(gè)節(jié)點(diǎn),那么需要逆序打印的話可以考慮用遞歸的方法.參考代碼如下:

void print(LinkedList &L){
    if(L != NULL){
        print(L->next);
        printf("%d ", L->data);
    }
}

(2)有一個(gè)int型數(shù)組,里面保存的是個(gè)位數(shù)的int數(shù)據(jù)(即0~9的數(shù)字), N <= 9, 數(shù)組的長(zhǎng)度為N,另給出一個(gè)int i, 不能使用其他變量,設(shè)計(jì)一個(gè)算法,求出數(shù)組中的最小者.

解析:意思是,只能用數(shù)組本身和i變量,求出其最小值.下面給出一個(gè)以前常用的一個(gè)求數(shù)組最小值的算法,如下:

#include <iostream>
#include <stdio.h>

using namespace std;

#define N 10

int main() {

    int a[N];

    for(int i = 0; i < N; i++) {
        scanf("%d", &a[i]);
    }

    int min1 = a[0];

    for(int j = 1; j < N; j++) {
        if(a[j] < min1) {
            min1 = a[j];
        }
    }

    cout << min1 << endl;
    
    return 0;
}

從以上代碼可以看出,需要找出最小值,必須有一個(gè)保存當(dāng)前循環(huán)的最小值的變量,而題目只給了一個(gè)i,所以i必須分成兩半用.
重新研究題目意思可以知道,數(shù)組保存的數(shù)據(jù)是一個(gè)個(gè)位數(shù),也就是說(shuō)是一個(gè)十位數(shù)是0的數(shù),可以這樣分開i,用i的十位數(shù)保存當(dāng)前的循環(huán),i的個(gè)位數(shù)保存當(dāng)前的最小值,因?yàn)樽钚≈禐閭€(gè)位數(shù),所以i可以完成保存兩個(gè)變量的使命,參考代碼如下,注釋已經(jīng)很詳細(xì)了:

void FindMin(int a[], int &i){
    i = a[0]; //初始時(shí)保存第一個(gè)數(shù)
    while(i / 10 < N){
    //循環(huán)繼續(xù)的條件,初始時(shí)i = a[0],十位為0, 每次循環(huán)之后對(duì)十位加1即可.
        if(i % 10 > a[i/10]){
            //用i個(gè)位上的數(shù)字,即當(dāng)前最小值,和a中第i/10(i的十位數(shù),即當(dāng)前比較的a數(shù)組元素的下標(biāo))個(gè)數(shù).
            //若a[..]更小,則更新i的個(gè)位數(shù)字
            i = i - i % 10;
            i = i + a[i/10];
        }
        i += 10;//即十位上數(shù)字加1,進(jìn)入下一個(gè)比較.
    }
    i = i % 10;//獲取個(gè)位上的數(shù)字,即最小值
}

今天就到這里了,完整的代碼后續(xù)會(huì)更新此到這里.謝謝~~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挡毅,一起剝皮案震驚了整個(gè)濱河市蒜撮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跪呈,老刑警劉巖段磨,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異耗绿,居然都是意外死亡苹支,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門缭乘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沐序,“玉大人琉用,你說(shuō)我怎么就攤上這事堕绩。” “怎么了邑时?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵奴紧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我晶丘,道長(zhǎng)黍氮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任浅浮,我火速辦了婚禮沫浆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滚秩。我一直安慰自己专执,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布郁油。 她就那樣靜靜地躺著本股,像睡著了一般攀痊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拄显,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天苟径,我揣著相機(jī)與錄音,去河邊找鬼躬审。 笑死棘街,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盒件。 我是一名探鬼主播蹬碧,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼炒刁!你這毒婦竟也來(lái)了恩沽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤翔始,失蹤者是張志新(化名)和其女友劉穎罗心,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體城瞎,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渤闷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脖镀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片飒箭。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蜒灰,靈堂內(nèi)的尸體忽然破棺而出弦蹂,到底是詐尸還是另有隱情,我是刑警寧澤强窖,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布凸椿,位于F島的核電站,受9級(jí)特大地震影響翅溺,放射性物質(zhì)發(fā)生泄漏脑漫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一咙崎、第九天 我趴在偏房一處隱蔽的房頂上張望优幸。 院中可真熱鬧,春花似錦褪猛、人聲如沸网杆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)跛璧。三九已至严里,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間追城,已是汗流浹背刹碾。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留座柱,地道東北人迷帜。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像色洞,于是被迫代替她去往敵國(guó)和親戏锹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容