顺序表的链式存储结构

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//如果需要良好的封装的话就需要两个结构
typedef struct ListNode {
    int data;
    struct ListNode* next;
};

typedef struct LinkList {
    ListNode* head;
    int length;
};


ListNode* init_listnode(int val) {
    ListNode* p = (ListNode*)malloc(sizeof(ListNode));
    p->data = val;
    p->next = NULL;
    return p;
}
LinkList* init_linklist() {
    LinkList* l = (LinkList*)malloc(sizeof(LinkList));
    l->head->next = NULL;
    //虚拟头节点的话,如果往后面第二个位置插入,就往后移动几个
    l->length = 0;
    return l;
}
void clear_listnode(ListNode* node) {
    if (node == NULL) return;
    //如果链表的数据域也是malloc出来的,也要free数据域
    free(node);
    return;
}


void clear_linklist(LinkList* l) {
    if (l == NULL) {
        return;
    }
    ListNode* p = l->head->next, * q;
    while (p) {
        //销毁的同时要有一个q来储存删除结点的后面一个结点
        q = p->next;
        clear_listnode(p);//删除相应的结点
        p = q;//这就走到了下一个结点
    }
    free(l);
    return;
}

int insert(LinkList* l, int ind, int val) {
    if (l == NULL) return 0;
    if (ind<0 || ind>l->length) {
        return 0;
    }
    ListNode* p = l->head,*node = init_listnode (val);
    while (ind--) {
        p = p->next;

    }
    node->next = p->next;
    p->next = node;
    l->length++;
    return;
}

int erase(LinkList* l, int ind) {
    if (l == NULL) return 0 ;
    if (ind < 0 && ind >= l->length) return 0;
    //后面就开始删除结点了
    ListNode* p = l->head, *q;
    while (ind--) {
        p = p->next;
    }
    //现在p就指向了待删除结点的前一个位置
    q = p->next->next;
    clear_listnode(p->next);
    p->next = q;
    l->length--;
    return 1;
}

void OutPut(LinkList* l) {
    printf("LinkList(%d) : ", l->length);
    for (ListNode* p = l->head->next; p; p = p->next) {
        printf("%d -> ", p->data);
    }
    printf("NULL\n");
    return;
}

//上面是链表的相关定义
int main() {
    LinkList* l = init_linklist();
    srand(time(0));
    for (int i = 0; i <30 ; i++) {
        int op = rand() % 4;
        int ind = rand() % (l->length + 1);
        int val = rand() % 100;
        switch (op) {
        case 0:
        case 1:
        case 2: {
            printf("insert %d at %d to LinkList %d\n", val, ind, insert(l, ind, val));

        }break;
        case 3: {
            printf("erase item at %d from LinkList = %d\n", ind, erase(l, ind));

        }break;
        }
        OutPut(l);
    }
}

这个与mooc陈越上面的链式表还是有很大的不同的。这个版本的封装程度大了很多。而且对链表进行了两次封装。个人觉得这种的更好。整体封装程度更高,也更好理解。做一个积累。如果只说说上面这个的话感觉太水了一点,后面再补一个循环链表的内容。

 

 发现了没,单向循环链表头指针中储存的是循环链表中的尾结点的地址。为什么要这样设置呢?

如果假设现在这个head指向1号结点的话,如果要往头插入结点的话,那就需要这个head指向头的前一个结点也就是6号结点。如果要完成这个操作的话不就要遍历一圈了。如果一开始就是指向6号结点的话,要往头插入结点就直接插入就好了,不需要再走一大圈了。

还有就是如果我要往第i个位置插入,只要让这个head指针往前走i步就可以插入了(这个时候停在i-1的位置)。这个和之前的虚拟头节点的用法其实是一样的。

 

posted @ 2022-02-27 11:02  prize  阅读(94)  评论(0)    收藏  举报