数据结构和算法(双向链表和双向循环链表)

471 阅读6分钟

双向链表

双向链表也称双链表,每个数据结点都有两个指针,分别指向直接前驱和直接后继。所以从双向链表的任何结点开始都可以方便的找到他的前驱和后继。

双向链表的每个结点如图所示:

双向链表由N个此类结点构成:

定义双向链表(C语言):包括前驱,后继,还有数据

typedef struct Node{
    struct Node *prior;
    Element data;
    struct Node *next;
}Node;

typedef struct Node * ListNode;

接下来,创建一个双向链表:

思路:
1.创建一个首元结点(为了方便操作,不用经常挪动头指针)
2.在链表中加入数据(单纯的为了后面的操作方便,也可以先初始化,之后再插数据)

初始化代码如下

Status creatListNode(ListNode *L){
    *L = (ListNode)malloc(sizeof(Node));
    if (!(*L)) return ERROR;
    
    (*L)->data = 0;
    (*L)->prior = NULL;
    (*L)->next = NULL;
    
    //初始化这里直接增加数据,可以方便查询
    ListNode p = *L;
    
    for (int i = 0; i<5; i++) {
        ListNode temp = (ListNode)malloc(sizeof(Node));
        temp->next = NULL;
        temp->prior = NULL;
        
        temp->data = i;
        p->next = temp;
        temp->prior = p;
        
        p = p->next;
    }
    return OK;
}

为了方便操作调试,加了个打印链表的方法:

void printListNode(ListNode L){
    printf("打印链表\n");
    ListNode temp = L->next;
    
    if (temp == NULL) {
        printf("打印链表为空\n");
        return;
    }
    
    while (temp) {
        printf("%d\t",temp->data);
        temp = temp->next;
    }
    printf("\n");
}

下面说下插入结点:

思路:
1、首先找到插入结点的位置。
2、是不是在链表尾部(这个就直接看代码就好了)
3、temp的next指向p的next
4、p的next的prior指向temp
5、p的next指向temp
6、temp的prior指向p
如下图所示:

Status insertListNode(ListNode *L, int place, Element data){
    ListNode p = *L;
    //1. 插入的位置不合法 为0或者为负数
    if(place < 1) return ERROR;
    
    ListNode temp = (ListNode)malloc(sizeof(Node));
    temp->data = data;
    temp->prior = NULL;
    temp->next = NULL;
    
    //找到前驱 p
    for (int i = 1;i < place && p; i++) {
        p = p->next;
    }
    //灭有
    if(p==NULL) return ERROR;
    //尾部
    if (p->next == NULL) {
        p->next = temp;
        temp->prior = p;
    }else{
        //插入
        temp->next = p->next;
        p->next->prior = temp;
        temp->prior = p;
        p->next = temp;
    }
    return OK;
}

接下来是删除代码:
老规矩--思路:
1、找到要删除位置的前一个
2、p的next指向要删除结点的next
3、删除结点的next的prior指向p
4、看下面的图(已经很生动了。。)

代码:

Status deleteListNode(ListNode *L, int place){
    if(place < 0) return ERROR;
    ListNode p = *L;
    int i;
    for (i = 1; i<place && p; i++) {
        p = p->next;
    }
    if (place>i || p == NULL) {
        return ERROR;
    }
    //要删除的结点
    ListNode deleTemp = p->next;
    printf("要删除的值为:%d\n",deleTemp->data);
    //删除
    p->next = deleTemp->next;
    if (deleTemp->next != NULL) {
        deleTemp->next->prior = p;
    }
    free(deleTemp);
    return OK;
}

双向链表的查找:

思路:
 简单来说就是把值传过来,然后遍历,找一样的。。😃

还是直接上代码吧

Status searchListNode(ListNode L, Element data){
    int j = 0;
    ListNode p = L->next;
    for (int i = 1;p != NULL ; i++) {
        if (p->data == data) {
            printf("此数值存在,位置是%d\n",i);
            j = 1;
            break;
        }
        p = p->next;
    }
    if (j == 0) {
        printf("没有发现此数值\n");
    }
    return OK;
}

给大家看下main函数:

    printf("双向链表\n");
    
    ListNode L;
    Status iStatus;
    int place;
    Element value;
    
    //创建双向链表
    printf("创建双向链表\n");
    iStatus = creatListNode(&L);
    printListNode(L);
    
    //插入
    printf("输入插入元素的位置和值,中间用空格隔开\n");
    scanf("%d %d",&place,&value);
    iStatus = insertListNode(&L,place,value);
    printListNode(L);
    
    //删除
    printf("输入删除的位置\n");
    scanf("%d",&place);
    iStatus = deleteListNode(&L, place);
    printListNode(L);
    
    //删除指定元素
    printf("删除指定元素\n");
    scanf("%d",&value);
    iStatus = deleData(&L, value);
    printListNode(L);
    
    //查找
    printf("查找元素\n");
    scanf("%d",&value);
    iStatus = searchListNode(L, value);

控制台输出:

双向链表
创建双向链表
打印链表
0	1	2	3	4	
输入插入元素的位置和值,中间用空格隔开
1 99
打印链表
99	0	1	2	3	4	
输入删除的位置
1
要删除的值为:99
打印链表
0	1	2	3	4	
删除指定元素
4
打印链表
0	1	2	3	
查找元素
3
此数值存在,位置是4

ok,双向链表就告一段落了。开始双向循环链表,😝

双向循环链表

双向循环链表和双向链表的区别简单理解就是首尾相连了。

一个空的双向循环链表如下:

正常的双向循环链表:

上图所示,大家应该理解双向循环链表是什么样子了。

接下来创建双向循环链表:

Status createListNode(ListNode *L){
    *L = (ListNode)malloc(sizeof(Node));
    if (!(*L)) return ERROR;//么有创建成功。。(这种情况基本不会存在,但是考虑代码严谨性,我还是写上了。。)
    
    (*L)->data = 0;
    (*L)->next = (*L);
    (*L)->prior = (*L);
    
    ListNode p = (*L);
    //我加点数据
    for (int i = 1; i < 11; i++) {
        ListNode temp = (ListNode)malloc(sizeof(Node));
        temp->data = i;
        
        p->next = temp;
        temp->prior = p;
        
        temp->next = (*L);
        (*L)->prior = temp;
        
        p = p->next;
    }
    return OK;
}

然后--双向循环链表的插入:

双向循环链表的插入和双向链表的插入差不多,就是要注意下首尾情况
废话我就不说了😄。直接上代码:
Status insertListNode(ListNode *L, int place, Element data){
    //是否合法
    if (place<0) return ERROR;
    
    ListNode p = *L;
    int i;
    
    for (i = 1; p->next != *L && i < place; i++) {
        p = p->next;
    }
    
    if (i > place)  return ERROR;
    
    //创建新结点
    ListNode tempNode = (ListNode)malloc(sizeof(Node));
    //6.temp 结点为空,则返回error
    if (tempNode == NULL) return ERROR;
    tempNode->data = data;
    
    tempNode->next = p->next;
    p->next->prior = tempNode;
    
    p->next = tempNode;
    tempNode->prior = p;
    
    return OK;
}

双向循环链表的删除

同样类似于双向链表,注意下首尾情况即可。
代码如下:
Status deleListNode(ListNode *L, int place){
    int i;
    ListNode p = (*L)->next;
    
    if (*L == NULL) {
        return  ERROR;
    }
    
    if (p->next == (*L)) {
        
        free(*L);
        (*L) = NULL;
        return OK;
    }
    for (i = 1; i < place; i++) {
        p = p->next;
    }
    
    printf("要删除的数据是%d\n",p->data);
    
    p->prior->next = p->next;
    p->next->prior = p->prior;
    free(p);
    return OK;
}
为了方便调试,写了个打印方法
void printListNode(ListNode L){
    printf("打印链表\n");
    if (L == NULL) {
        printf("双向循环链表为空!\n");
        return;
    }
    ListNode p = L->next;
    
    for (int i=0; p != L; i++) {
        
        printf("%d  ",p->data);
        p = p->next;
    }
    printf("\n");
}

main方法:

    printf("双向循环链表\n");
    
    ListNode L;
    Status iStatus;
    int place;
    Element data;
    
    printf("创建链表\n");
    iStatus = createListNode(&L);
    printListNode(L);
    
    printf("插入位置和数据中间空格隔开\n");
    scanf("%d %d",&place,&data);
    iStatus = insertListNode(&L, place, data);
    printListNode(L);
    
    printf("删除数据,输入删除的位置\n");
    scanf("%d",&place);
    iStatus = deleListNode(&L, place);
    printListNode(L);

输出如下:

双向循环链表
创建链表
打印链表
1  2  3  4  5  6  7  8  9  10  
插入位置和数据中间空格隔开
1 99
打印链表
99  1  2  3  4  5  6  7  8  9  10  
删除数据,输入删除的位置
2
要删除的数据是1
打印链表
99  2  3  4  5  6  7  8  9  10