線性表(順序存儲/鏈式存儲)

數據結構中按邏輯結構分爲4類:集合、線性表、樹、圖。按存儲結構分爲2類:順序存儲、鏈式存儲。

一些輔助定義:

#define MAXSIZE 20

#define OK 0
#define ERROR 1

typedef int Status;
typedef int ElemType;

線性表結構定義:
//順序存儲
typedef struct
{
    ElemType data[MAXSIZE];
    int length;
}SqList;
//鏈式存儲節點
typedef struct Node
{
    ElemType data;
    struct Node *next;
}Node,*PNode;
//靜態鏈式線性表
typedef struct {
    ElemType data;
    int cur;
}Component,StaticLinkList[MAXSIZE];


所有代碼都是可運行的,爲方便測試,現附上main()函數。

int main(int argc, const char * argv[])
{
    PNode pNode = NULL;
    PNode _pNode = NULL;
    Status ret = ERROR;
    ret = CreatLinkList(&pNode, MAXSIZE);
    if (ret) {
        printf("創建鏈表失敗!\n");
        return ERROR;
    }
    ret = CreatLinkList(&_pNode, MAXSIZE);
    if (ret) {
        printf("創建鏈表失敗!\n");
        return ERROR;
    }
//    PrintLinkList(pNode);
//    ReverseLinkList(&pNode);
//    PrintLinkList(_pNode);
//   UnionLinkList(&pNode, _pNode);
//    PrintLinkList(pNode);
    PNode entryNode = NULL;
    PNode *mtpNode = (PNode *)malloc(sizeof(Node));
    ret = IsCircleLinkList(pNode, mtpNode);
    if (!ret) {
        printf("該鏈表無環!\n");
    }else{
        entryNode = FindEntryNode(pNode, (*mtpNode));
        printf("該鏈表有環!\n環節點的值爲:%d\n",entryNode->data);
    }
    return 0;
}


1.線性表的順序存儲

元素插入

Status ListInsert(SqList *L, int i, ElemType e)
{
    i -= 1;
    
    if (L == NULL || L->length<i || i < 0 || L->length >= MAXSIZE) {
        printf("鏈表爲空,或者插入位置非法!\n");
        return ERROR;
    }
    
    for (int j = L->length-1; j >= i; j--) {
        L->data[j+1] = L->data[j];
    }
    
    L->data[i] = e;
    L->length += 1;
    
    return OK;
}


元素刪除
Status ListDelete(SqList *L, int i, ElemType *e)
{
    if (L == NULL || i < 1 || i > L->length) {
        printf("鏈表爲空,或者刪除位置溢出!");
        return ERROR;
    }
    
    *e = L->data[i -1];
    
    for (int j = i; j < L->length; j++) {
        L->data[j-1] = L->data[j];
    }
    L->length -= 1;
    
    return OK;
}

2.線性表的鏈式存儲(有兩種實現方式(動態鏈表/靜態鏈表),其中靜態鏈表用數組實現,數組下標相當於動態鏈表中的指針域)

//動態鏈表

創建鏈表

Status CreatLinkList(PNode *ppNode, int n)
{
    if (n < 1) {
        printf("參數錯誤!\n");
        return ERROR;
    }
    *ppNode = (PNode)malloc(sizeof(Node));//頭結點
    if (!(*ppNode)) {
        printf("內存分配失敗!\n");
        return ERROR;
    }
    (*ppNode)->data = 0;
    (*ppNode)->next = NULL;
    
    int i = 0;
    PNode _ppNode = NULL;
    PNode head_pNode = (*ppNode);
    for (i = 0; i < n; i++) {
        _ppNode = (PNode)malloc(sizeof(Node));
        if (_ppNode) {
            _ppNode->data = i;
            _ppNode->next = NULL;
            head_pNode->next = _ppNode;
            head_pNode = head_pNode->next;
        }else{
            printf("內存分配失敗!\n");
            return ERROR;
        }
    }
    (*ppNode)->data = i;
    
    return OK;
}


打印鏈表
void PrintLinkList(PNode pNode)
{
    PNode _ppNode = pNode->next;
    if (!pNode || !_ppNode) {
        printf("鏈表爲空!\n");
        return;
    }
    printf("打印鏈表:\n");
    while (_ppNode) {
        printf("%d\t",_ppNode->data);
        _ppNode = _ppNode->next;
    }
    printf("\n打印結束\n");
}

鏈表逆序(這個應該用前插法,我這寫法太笨了,大家就不要看了。)

void ReverseLinkList(PNode *ppNode)
{
    if (!ppNode || !(*ppNode)) {
        printf("鏈表爲空!\n");
        return;
    }
    PNode left_pNode = NULL;//反轉鏈表用
    PNode right_pNode = NULL;
    PNode _ppNode = (*ppNode)->next;
    
    while (_ppNode) {
        right_pNode = _ppNode->next;
        _ppNode->next = left_pNode;
        left_pNode = _ppNode;
        _ppNode = right_pNode;
    }
    (*ppNode)->next = left_pNode;
}


兩個有序鏈表合併成一個有序鏈表
void UnionLinkList(PNode *ppNode, PNode pNode)
{
    if (!ppNode || !(*ppNode) || !pNode) {
        printf("鏈表非法!\n");
        return;
    }
    PNode tmp_pNode = pNode->next;//保存從pNode中取出的節點
    PNode pri_pNode = (*ppNode);//保存當前遍歷節點的前一個節點
    PNode cur_pNode = pri_pNode->next;//保存當前節點
    pNode = pNode->next;
    while (tmp_pNode && cur_pNode)
    {
        while (cur_pNode)
        {
            if (tmp_pNode->data <= cur_pNode->data)
            {
                pNode = pNode->next;
                tmp_pNode->next = cur_pNode;
                pri_pNode->next = tmp_pNode;
                
                tmp_pNode = pNode;
                break;
            }
            else
            {
                pri_pNode = cur_pNode;
                cur_pNode = cur_pNode->next;
            }
        }
    }
    if (tmp_pNode && !cur_pNode) {
        pri_pNode->next = tmp_pNode;
    }

}

判斷鏈表是否存在環

原理:通過快慢指針實現。設慢指針slow_pNode(一次走一步),快指針fast_pNode(一次走兩步)。 假設當慢指針剛進入換的時候,快指針已近在環中走過了m步。則,當慢指針在環中走過 i 步時,快指針走過了m+2 * i步,此時快指針比慢指針多走了step = m + 2*i - i = m + i步。一定存在一個 i 值,使得step = n(環的長度)。代碼如下:

Status IsCircleLinkList(PNode pNode, PNode *mtpNode)
{
    if (!pNode){
        printf("鏈表爲空!\n");
        return ERROR;
    }
    PNode slow_pNode = pNode;
    PNode fast_pNode = pNode;
    
    while (fast_pNode)
    {
        if (!fast_pNode->next) {
            return OK;
        }
        fast_pNode = fast_pNode->next->next;
        slow_pNode = slow_pNode->next;
        if (fast_pNode == slow_pNode)//有環
        {
            (*mtpNode) = fast_pNode;//把相遇節點傳出來,便於尋找入口節點。
            return ERROR;
        }
        else if (!fast_pNode)
        {
            return OK;
        }
    }
    return OK;
}

尋找入口節點:

原理:以相遇節點爲標識,在邏輯上將有環鏈表分成兩個單鏈表pNode 和 _pNode。然後計算兩個鏈表長度的差step,讓較長鏈表先走step步,之後兩個鏈表挨個節點進行比較,直至找到相同節點,即爲入口節點(該原理類似從兩個不同的單鏈表中找出其相同的節點)。

圖例如下:(最後的節點9不是指向NULL,而是指向前面的節點5)

1-->2-->3-->4--5-->6-->7-->8-->9

             ^                           |

                           |____________|

此時,假設相遇節點爲7,以7爲標示,將該鏈表從邏輯上分爲以下兩個鏈表:

1-->2-->3-->4--5>--6>-->7

7-->8-->9-->5-->6

實現代碼:(pNode相當於上面的1節點地址,_pNode相當於7節點地址)

PNode FindEntryNode(PNode pNode, PNode _pNode)
{
    int len1 = 0, len2 = 0;
    
    PNode tmp_pNode = pNode->next;
    while (tmp_pNode != _pNode) {
        ++len1;
        tmp_pNode = tmp_pNode->next;
    }
    len1 += 1;
    
    tmp_pNode = _pNode->next;
    while (tmp_pNode != _pNode) {
        ++len2;
        tmp_pNode = tmp_pNode->next;
    }
    len2 += 1;
    
    int step = abs(len1 - len2);
    while (step) {
        pNode = pNode->next->next;
        step--;
    }
    tmp_pNode = _pNode;//由環解開的鏈表
    while (pNode != tmp_pNode) {//沒走到虛擬的鏈表尾部,實際就是從相遇節點把鏈表分成兩個假象的單鏈表
        if(pNode == _pNode){
            break;
        }
        pNode = pNode->next;
        _pNode = _pNode->next;
    }
    return pNode;
}

//靜態鏈表

實現原理如圖:


初始化鏈表

Status InitStaticLinkList(StaticLinkList space)
{
    int i;
    for (i = 0; i < MAXSIZE - 1; i++) {
        space[i].cur = i+1;
    }
    space[MAXSIZE -1].cur = 0;
    return OK;
}


分配一個節點(實際就是返回一個能用的節點位置)
int Malloc(StaticLinkList space)
{
    if (space == NULL) {
        return 0;
    }
    int i = space[0].cur;
    if (i) {
        space[0].cur = space[i].cur;
    }
    return i;
}


釋放節點

void Free(StaticLinkList space, int j)
{
    space[j].cur = space[0].cur;
    space[0].cur = j;
}

返回靜態鏈表長度
int StaticLinkListLength(StaticLinkList L)
{
    int i = L[MAXSIZE -1].cur;
    int length = 0;
    while (i) {
        ++length;
        i = L[i].cur;
    }
    return length;
}


插入
Status StaticListInsert(StaticLinkList L, int i, ElemType e)
{
    int cur = Malloc(L);
    int k = MAXSIZE -1;
    if (L == NULL || i < 1 || i > StaticLinkListLength(L)+1) {
        return ERROR;
    }
    if (cur) {
        
        L[cur].data = e;
        for (int m = 0; m < i - 1; m++) {
            k = L[k].cur;
        }
        L[cur].cur = L[k].cur;
        L[k].cur = cur;
        return OK;
    }
    return ERROR;
}


刪除
Status StaticLinkListDelete(StaticLinkList L, int i)
{
    if (L == NULL || i < 1 || i > StaticLinkListLength(L)) {
        return ERROR;
    }
    int k = MAXSIZE - 1;
    for (int m = 0; m < i - 1; m++) {
        k = L[k].cur;
    }
    int j = L[k].cur;
    L[k].cur = L[j].cur;
    Free(L, j);
    return OK;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章