4、数据结构与算法 - 双向链表和双向循环链表

大纲

双向链表(增删改查)

双向循环链表(增删)

一、双向链表

双向链表的结点有连个指针域,一个(prior)指向钱一个结点,一个(next)指向后一个结点。

​​

#include <stdio.h>
#include "string.h"
#include "ctype.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"

#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OK 1

#define MAXSIZE 20 //存储空间初始分配量

typedef int Status;//Status 是函数的类型,其值是函数结果状态代码l,如OK等
typedef int ElemType;//ElemType 类型根据实际情况而定,这里假设为int

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

typedef struct Node * LinkList;

Status createList(LinkList *L){

1、 双向链表创建初始化

*L = (LinkList)malloc(sizeof(Node));
    if (*L == NULL) return ERROR;
    
//    头结点
    (*L)->prior = NULL;
    (*L)->data = -1;//不会用到设置不设置都行
    (*L)->next = NULL;
    
//    新增结点
    LinkList p = *L;
    for (int i=10; i<20; i++) {
        
        LinkList temp = (LinkList)malloc(sizeof(Node));
        if (temp == NULL) return ERROR;
        temp->prior = NULL;
        temp->data = i;
        temp->next = NULL;
        
//        建立一个双向链表关系
        p->next = temp;
        temp->prior = p;
        p = p->next;
    }
    return OK;
}

2、双向链表的遍历

//2、遍历双向链表
void display(LinkList L){
    LinkList temp = L->next;
    if (temp == NULL) {
        printf("双向链表为空\n");
        return;
    }
    
    while (temp) {
        printf("%d  ", temp->data);
        temp = temp->next;
    }
    printf("\n");
}

3、双向链表的插入

思路:

1、判断插入位置是否合法(<1)

2、创建一个新结点(temp),并赋值。

3、创建一个指针(p)指向头结点,用于变量链表找到插入位置前一个结点。

4、找到要插入位置的前一个结点,并判断是否超出链表长度(p==NULL)。

5、判断是否在链表尾部(尾部的话只需要处理插入结点的前驱指向,和前一个结点的后驱指向就行)。

6、插入位置不在尾结点。(例如: a b 之间插入c)

(1)、将插入位置后一个结点的前驱指向要插入的结点。 b->prior = c;

(2)、将要插入结点的后驱指向插入位置后一个结点。 c->next = b;

(3)、将插入前一个结点的后驱指向要插入的结点。a->next = c;

(4)、将要插入的结点的前驱指向插入位置前一个结点。c->prior = a;

(1)(2)顺序可换,(3)(4)顺序可换。但是整体的(1)(2) 和 (3)(4)的顺序不能换,因为换了之后就找不到后面的结点了,所以需要先指向后面的结点之后再处理前面结点。

Status ListInsert(LinkList *L,int i, ElemType data){
    //1、判断位置是否合法
//    有头结点所以如果插入位置小于0不合法,等于0就是插到头结点之前也是不合法位置
    if (i<1) return ERROR;
    
//    2、创建结点
    LinkList temp = malloc(sizeof(Node));
    temp->prior = NULL;
    temp->data = data;
    temp->next = NULL;
    
//    3、将p指向指向 *L 头结点
    LinkList p = *L;
    
//    4、找到插入位置i 前一个结点
    for (int j=0; j<i; j++) {
        p = p->next;
    }
        
//    5.如果插入的位置超过了链表本身的长度返回错误
    if (p == NULL) return ERROR;
        
//    6、判断插入的位置是否是链表尾部
    if (p->next == NULL) {
        p->next = temp;
        temp->prior = p;
    }else{
        //(1)、将p->next(插入位置后一个结点)的前驱 prior = temp;
        p->next->prior = temp;
        //(2)、将temp->next 指向原来的p->next (插入位置后一个结点);
        temp->next = p->next;
        //(3)、将p-next 更新成新创建的需要插入的 temp;
        p->next = temp;
        temp->prior = p;
    }
    
    return OK;
}

4.1、删除双向链表指定位置上的结点

1、找到删除位置前一个结点

2、用一个指针指向删除结点

3、将删除位置的前后结点进行关联。

4、释放掉要删除的结点。

//指针e 将删除的值带回去。
Status ListDelete(LinkList *L,int i,ElemType *e){
    
    if (i<1) return ERROR;
    
    LinkList p = (*L);//指向头结点遍历找到删除位置前一个结点用
    
//    1、判断双向链表是否为空,如果为空返回 错误
    if (*L == NULL) return ERROR;
    
//    2、将指针p 指向删除位置前面的结点
    for (int j=1; j<i; j++) {
        p = p->next;
//        3、如果 p == NULL 返回错误
        if (p == NULL) return ERROR;
    }
    
//  4、创建临时指针 delTemp 指向被删除的结点,并将要删除的结点的data 赋值给 *e 带回
    LinkList delTemp = p->next;
    *e = delTemp->data;
    
//    5、p->next 等于要删除的结点的下一个结点
    p->next = delTemp->next;
    
//    6、如果删除的结点的下一个结点不为空,则将要删除的结点的下一个结点的前驱指向要删除的上一个结点
    if (delTemp->next != NULL) {
        delTemp->next->prior = p;
    }
    
//    7、删除delTemp 结点
    free(delTemp);
    
    return OK;
}

4.2、双向链表删除指定内容

Status ListDeleteValue(LinkList *L, int data){
    LinkList p = (*L)->next;//不从头结点开始,因为头结点data我们也赋的有值,目的不是删除头结点
    
//    1、遍历双向链表
    while (p) {
        
//        2、判断当前结点的数据域和data是否相等,若相等则删除该结点
        if (p->data == data) {
//            修改被删除结点的前驱结点的后继指针
            p->prior->next = p->next;
//            修改删除结点的后继结点的前驱指针
            if (p->next != NULL) {
                p->next->prior = p->prior;
            }
//            释放被删除的结点
            free(p);
//            退出循环
            break;
        }
//        不是要找的结点继续往后找
        p = p->next;
    }
    
    if (p == NULL) return ERROR;
    return OK;
}

5、双向链表查找

int selectElem(LinkList L, ElemType elem){
    LinkList p = L->next;
    int i =1;
    
    while (p) {
        if (p->data == elem) {
            return i;
        }
        i++;
        p = p->next;
    }
    return -1;
}

6、双向链表中更新结点 

Status replaceLinkList(LinkList *L, int index, ElemType newElem){
    
    if (index <1)return ERROR;
    
    LinkList p = *L;
    for (int i=1; i<index; i++) {
        p = p->next;
        if (p == NULL) return ERROR;
    }
    
    p->next->data = newElem;
    return OK;
}

二、双向循环链表

双向循环链表,与双向链表的区别就是:双向循环链表的尾结点的后驱指向头结点(这里为了方便处理加了头结点)。

双向循环链表的结点和双向链表的结点一样。

1、创建初始化

Status createList(LinkList *L)
{
    *L = (LinkList)malloc(sizeof(Node));
    if (*L == NULL) return ERROR;
    
    (*L)->prior = (*L);
    (*L)->next = (*L);
//    (*L)->data = -1;//这个地方给-1的目的是就是想让其为空,可以不写。
    
//    新增数据
    LinkList p = *L;
    for (int i=0; i<10; i++) {
//        1 temp 创建一个临时的结点
        LinkList temp = (LinkList)malloc(sizeof(Node));
        if (temp == NULL) return  ERROR;
        temp->data = i;
        
//        2、为新增的结点建立双向链表关系
//        (1)、temp 是p的后继
        p->next = temp;
//        (2)、temp 的前驱是p
        temp->prior = p;
//        (3)、temp的后继是 *L
        temp->next = (*L);
//        (4)、p 的前驱是新建的 temp
        p->prior = temp;
//        (5)、 p 要记录最后的结点的位置,方便下次插入
        p = p->next;
    }
    return OK;
}

2、遍历

void display(LinkList L){
    
    LinkList temp = L->next;
    if (temp == NULL) {
        printf("打印的双向链表为空!\n");
    }
    printf("双向链表的内容:\n");
    while (temp != L) {
        printf("%d    ",temp->data);
        temp = temp->next;
    }
    printf("\n");
}

3、插入元素

//0是头结点,不是位置,位置从1开始
Status ListInsert(LinkList *L, int i, ElemType data){
    
//    if (i<1) return ERROR;
    
//    1、创建指针p 指向双向链表头
    LinkList p = (*L);
    
//    2、双向链表为空,返回error
    if (*L == NULL) return ERROR;

//    3、找到插入位置的前一个 结点
    for (int j=1; j<i && p; j++) {
        p = p->next;
    }
    
//    4、越界判断,后面的是null
    if (p==NULL) return ERROR;
    
//    5、创建新结点 temp
    LinkList temp = (LinkList)malloc(sizeof(Node));
    if (temp == NULL) return ERROR;
    temp->data = data;
//    6、将结点temp 的前驱结点指向 p
    temp->prior = p;
//    7、将temp 的后续结点指向 p->next
    temp->next = p->next;
//    8、p 的后续结点为新结点 temp
    p->next = temp;
    
//    9、如果temp 结点不是最后一个结点
    if (*L != temp->next) {
    //temp 结点的下一个结点的前驱指向 temp
        p->next->prior = temp;
    }else{
        (*L)->prior = temp;
    }
    
    return OK;
}

4、删除元素

Status ListDelete(LinkList *L, int i,ElemType *e){
    int k =1;
//    1、
    if (*L == NULL) return ERROR;
    
    LinkList p = (*L)->next;
//    2、如果删除的只剩下首元结点了,则直接将 *L置空
    if (p->next == (*L)) {
        free(*L);
        (*L) = NULL;
        return OK;
    }
    
//    3、找到要删除的结点
    while (k<i) {
        p = p->next;
        k++;
    }
    
//    4、给 e 赋值要删除结点的数据域
    *e = p->data;
    
//    5、修改被删除结点的前一个结点的后继指向
    p->prior->next = p->next;
//    6、修改被删除结点的后一个指针的前驱指向
    p->next->prior = p->prior;
    
//    删除结点
    free(p);
    return OK;
}

发布了104 篇原创文章 · 获赞 13 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/shengdaVolleyball/article/details/105468860