1. 程式人生 > >資料結構入門-離散儲存(連結串列)

資料結構入門-離散儲存(連結串列)

一、預備知識:typedef

基本使用

#include <stdio.h>

typedef int AAA; // 為int再重新取一個名字,AAA就等於int

typedef struct Student
{
    int sid;
    char name[100];
    char sex;
}ST;

int main(void)
{

    int i = 10; // 等價於 AAA = 10; 

    struct Student st;  // 等價於  ST st;
    struct Student * ps = &st;  // 等價於 ST * ps = &st;


    ST st2;
    st2.sid = 10;

    printf("%d \n", st2.sid);

    return 0;
}

也可以這樣使用,這樣更加的方便

#include <stdio.h>


typedef struct Student
{
    int sid;
    char name[100];
    char sex;
}* PST;  // PST等價於 typedef struct * 

int main(void)
{
    struct Student st;
    PST ps = &st;

    ps->sid = 99;
    printf("%d\n", ps->sid);

    return 0;
}

還可以把上面的兩個結合起來

#include <stdio.h>


typedef struct Student
{
    int sid;
    char name[100];
    char sex;
}* PST , STU;  // PST等價於 typedef struct *  , STU代表了typedef struct

int main(void)
{
    STU st;  // 等價於struct Student st
    PST ps = &st;

    ps->sid = 99;
    printf("%d\n", ps->sid);

    return 0;
}

二、離散儲存(連結串列)

定義:n個節點離散分配,彼此通過指標相連,每一個節點只有一個前驅節點和一個後續節點,首節點沒有前驅節點,尾節點沒有後續節點

專業術語:

  1. 首節點:第一個有效節點
  2. 尾節點:最後一個有效節點
  3. 頭節點:首節點前面
  4. 頭指標:指向頭節點的指標變數
  5. 尾指標:指向尾節點的指標變數

注意:頭節點的資料型別和首節點型別一樣,頭節點裡面沒有存放有效資料,沒有實際含義,為了方便對連結串列的操作

如果通過希望一個函式來對連結串列進行處理,我們至少需要接收連結串列的那些引數

只需要一個引數:頭指標

因為我們可以通過頭指標推算出連結串列的其他所有引數

每一個連結串列節點的資料型別如何表示

#include <stdio.h>

typedef struct Node
{
    int data;  // 資料域
    struct Node * pNext; // 指標域 指向了和它本身型別一樣的另外一個節點
}NODE , *PNODE;
// NODE 等價於struct Node
// PNODE 等價於struct Node *

int main(void)
{
    return 0;
}

分類:

  1. 單鏈表
  2. 雙鏈表:每一個節點有兩個指標域
  3. 迴圈連結串列:能通任何一個節點找到其他所有的節點
  4. 非迴圈連結串列

演算法:

  1. 遍歷
  2. 查詢
  3. 清空
  4. 銷燬
  5. 求長度
  6. 排序
  7. 刪除節點
  8. 插入節點

演算法

狹義的演算法是與資料的儲存方式密切相關

廣義的演算法與資料的儲存方式無關

泛型

利用某種技術達到的效果就是:不同的儲存方式,執行的操作是一樣的

多敲程式碼,熟練的掌握,並進行改進

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>



typedef struct Node
{
    int data;
    struct Node * pNext;
}NODE , *PNODE;   // NODE等價於struct Node , PNODE等價於struct Node *


PNODE create_list(void);
void traverse_list(PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE pHead);
bool insert_list(PNODE , int ,int); // 第二個是插入位置,第三個是插入的值
bool delete(PNODE , int, int*); // 第二個是刪除的位置,第三個是所刪除位置的值
void sort_list(PNODE pHead);



int main(void)
{
    PNODE pHead = NULL; // 等價於struct Node * pHead = NULL;

    pHead = create_list(); // 建立一個非迴圈單鏈表,並將該連結串列的頭節點地址付給pHead

    // if(is_empty(pHead))
    //  printf("連結串列為空\n");
    // else
    //  printf("連結串列不為空\n");

    // printf("連結串列的長度為%d\n", length_list(pHead) );
    // sort_list(pHead);
    insert_list(pHead , 4, 99);

    int val;
    if(delete(pHead, 1 , &val))
    {
        printf("刪除成功,你刪除的是%d\n", val);
    }
    else
    {
        printf("刪除失敗\n");
    }
    
    traverse_list(pHead);


    return 0;
}


// 構建一個連結串列,並把頭節點地址值返回
PNODE create_list(void)
{
    int len;
    int i;
    int val; // 用來臨時存放使用者輸入的節點的值

    // 分配了一個不存放資料的頭結點
    PNODE pHead = (PNODE)malloc(sizeof(NODE));
    if (pHead == NULL)
    {
        printf("分配失敗,程式終止!\n");
        exit(-1);
    }

    // pTail始終執行的都是尾結點
    PNODE pTail = pHead;
    pTail->pNext = NULL;


    printf("請輸入你需要生成的連結串列節點的個數:\n");
    scanf("%d" , &len);

    for (i = 0; i < len; ++i)
    {
        printf("請輸入第%d個節點的值:", i+1);
        scanf("%d" , &val);

        PNODE pNew = (PNODE)malloc(sizeof(NODE));
        if (pNew == NULL)
        {
            printf("分配失敗,程式終止!\n");
            exit(-1);
        }

        pNew->data = val;
        pTail->pNext = pNew;
        pNew->pNext = NULL;
        pTail = pNew;
    }

    return pHead;
}

// 進行遍歷
void traverse_list(PNODE pHead)
{
    // 自定義一個指標用於遍歷
    PNODE p = pHead->pNext;
    while(p != NULL){
        printf("%d ",p->data );
        p = p->pNext;
    }
    return;
}


// 判斷連結串列是否為空
bool is_empty(PNODE pHead)
{
    if (pHead->pNext == NULL)
        return true;
    else
        return false;
}

// 連結串列的長度
int length_list(PNODE pHead)
{
    // 自定義一個指標用於計算連結串列的長度
    PNODE p = pHead->pNext;
    int len = 0;

    while(NULL != p)
    {
        ++len;
        p = p->pNext;
    }
    return len;
}

// 進行排序
void sort_list(PNODE pHead)
{
    // 這裡和陣列的排序差不多,思想是一樣的


    int i , j , t;
    PNODE p , q;

    int len = length_list(pHead);

    for (i = 0 , p = pHead->pNext; i < len -1; ++i , p = p->pNext)
    {
        for (j = i+1 , q = p->pNext; j < len; ++j , q = q->pNext)
        {
            if (p->data > q->data)
            {
                t = p->data;
                p->data = q->data;
                q->data = t;
            }
        }
    }

    return;

}



// 插入操作
// 在pHead所指向連結串列的第pos個節點的前面插入一個新的結點,該結點的值是val,pos從1開始
bool insert_list(PNODE pHead, int pos , int val)
{

    int i = 0;
    PNODE p = pHead;

    while(NULL != p && i < pos-1)
    {
        p = p->pNext;
        ++i;
    }

    if (i > pos-1 || NULL == p)
        return false;


    PNODE pNew = (PNODE)malloc(sizeof(NODE));
    if (NULL == pNew)
    {
        printf("動態分配記憶體失敗\n");
        exit(-1);
    }
    pNew->data = val;
    PNODE q = p->pNext;
    p->pNext = pNew;
    pNew->pNext = q;

    return true;
}

// 刪除操作
bool delete(PNODE pHead , int pos, int * pval)
{

    int i = 0;
    PNODE p = pHead;

    while(NULL != p->pNext && i < pos-1)
    {
        p = p->pNext;
        ++i;
    }

    if (i > pos-1 || NULL == p->pNext)
        return false;



    PNODE q = p->pNext;
    *pval = q->data;

    // 刪除p結點後面的結點
    p->pNext = p->pNext->pNext;
    free(q);
    q = NULL;

    return true;
}