单向链表和双向链表分析与操作

单链表和双链表
链表结构:

优点:
1、在程序中使用数组之前,必须事先知道数组的大小,增加数组的大小是一个耗时的过程,在运行时几乎不可能扩展数组的大小。而链表不需要提前声明链表的大小,链表的大小是随着使用的过程逐步增大的。
2、在空间的利用上链表相比数组要更加灵活,不会造成内存的大量浪费。
3、向链表中插入或从链表中删除一项的操作不需要移动很多项,只涉及常数个节点链的改变,时间复杂度为O(1)。
缺点:
由于在链表中,仅仅只有头节点和尾节点是可见的,因此要想查找某个节点,必须从头节点或尾节点一路找下去,时间复杂度至少为O(logn) (二分),不如数组快。

创建一个链表

头指针
一头指针是指链表指向第一个结点的指针,若链表有
头结点,则是指向头结点的指针。
一头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)。
一无论链表是否为空,头指针均不为空。一头指针是链表的必要元素。
头结点
一头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)。
一有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了。
单链表

储存结构
假设p是指向线性表第i个元素的指针,则该结点ai的数据域我们可以用p->data的值是一个数据元素,结点ai的指针域可以用p- >next来表示,p- >next的值是一个指针。
那么p->next指向谁呢?当然指向第i+1个元素!也就是指向ai+1的指针。
问题:
一如果p->data = ai,那么p->next->data =?
答案:p->next->data = ai+1。

创建一个单链表
1、声明一个头指针(如果有必要,可以声明一个头节点);
2、创建多个存储数据的节点,在创建的过程中,要随时与其前驱节点建立逻辑关系;

例如创建一个存储{1,2,3,4}且无头节点的单链表 实现增删改查

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

//1、声明结点结构
typedef struct Link {
    
    
    int  elem;         //数据域
    struct Link* next; //指针域
}link;


link* initLink();
//链表插入的函数,p是链表,elem是插入的结点的数据域,add是插入的位置
link* insertElem(link* p, int elem, int add);
//删除结点的函数,p代表操作链表,add代表删除节点的位置
link* delElem(link* p, int add);
//查找结点的函数,elem为目标结点的数据域的值
int selectElem(link* p, int elem);
//更新结点的函数,newElem为新的数据域的值
link* amendElem(link* p, int add, int newElem);
void display(link* p);

int main() {
    
    
    link* p = NULL;
    int address;
    //初始化链表(1,2,3,4)
    printf("初始化链表为:\n");
    p = initLink();
    display(p);

    printf("在第4的位置插入元素5:\n");
    p = insertElem(p, 5, 4);
    display(p);

    printf("删除元素3:\n");
    p = delElem(p, 3);
    display(p);

    printf("查找元素2的位置为:\n");
    address = selectElem(p, 2);
    if (address == -1) {
    
    
        printf("没有该元素");
    }
    else {
    
    
        printf("元素2的位置为:%d\n", address);
    }
    printf("更改第3的位置上的数据为7:\n");
    p = amendElem(p, 3, 7);
    display(p);

    return 0;
}

//2、创建链表(1,2,3,4)
link* initLink() {
    
    
    link* p = (link*)malloc(sizeof(link));//创建一个头结点
    link* temp = p;//声明一个指针指向头结点,用于遍历链表
    int i = 0;
    //生成链表
    for (i = 1; i < 5; i++) {
    
    
        link* a = (link*)malloc(sizeof(link));
        a->elem = i;
        a->next = NULL;
        temp->next = a;
        temp = temp->next;
    }
    return p;
}

//插入一个元素
link* insertElem(link* p, int elem, int add) {
    
    
    link* temp = p;//创建临时结点temp
    link* c = NULL;
    int i = 0;
    //首先找到要插入位置的上一个结点
    for (i = 1; i < add; i++) {
    
    
        if (temp == NULL) {
    
    
            printf("插入位置无效\n");
            return p;
        }
        temp = temp->next;
    }
    //创建插入结点c
    c = (link*)malloc(sizeof(link));
    c->elem = elem;
    //向链表中插入结点
    c->next = temp->next;
    temp->next = c;
    return  p;
}

//删除一个元素
link* delElem(link* p, int add) {
    
    
    link* temp = p;
    link* del = NULL;
    int i = 0;
    //遍历到被删除结点的上一个结点
    for (i = 1; i < add; i++) {
    
    
        temp = temp->next;
    }
    del = temp->next;//单独设置一个指针指向被删除结点,以防丢失
    temp->next = temp->next->next;//删除某个结点的方法就是更改前一个结点的指针域
    free(del);//手动释放该结点,防止内存泄漏
    return p;
}

//查找一个元素
int selectElem(link* p, int elem) {
    
    
    link* t = p;
    int i = 1;
    while (t->next) {
    
    
        t = t->next;
        if (t->elem == elem) {
    
    
            return i;
        }
        i++;
    }
    return -1;
}

//更新元素
link* amendElem(link* p, int add, int newElem) {
    
    
    int i = 0;
    link* temp = p;
    temp = temp->next;//tamp指向首元结点
    //temp指向被删除结点
    for (i = 1; i < add; i++) {
    
    
        temp = temp->next;
    }
    temp->elem = newElem;
    return p;
}
void display(link* p) {
    
    
    link* temp = p;//将temp指针重新指向头结点
    //只要temp指针指向的结点的next不是Null,就执行输出语句。
    while (temp->next) {
    
    
        temp = temp->next;
        printf("%d ", temp->elem);
    }
    printf("\n");
}

双链表

指针域:用于指向当前节点的直接前驱节点;
数据域:用于存储数据元素;
指针域:用于指向当前节点的直接后继节点。
双链表和单链表是差不多的,只是比单链表多操作一步

#include <stdio.h>
#include <stdlib.h>
typedef struct line {
    
    
    struct line * prior;
    int data;
    struct line * next;
}line;
//双链表的创建
line* initLine(line * head);
//双链表插入元素,add表示插入位置
line * insertLine(line * head, int data, int add);
//双链表删除指定元素
line * delLine(line * head, int data);
//双链表中查找指定元素
int selectElem(line * head, int elem);
//双链表中更改指定位置节点中存储的数据,add表示更改位置
line *amendElem(line * p, int add, int newElem);
//输出双链表的实现函数
void display(line * head);
int main() {
    
    
    line * head = NULL;
    //创建双链表
    printf("初始链表为:\n");
    head = initLine(head);
    display(head);
    //在表中第 3 的位置插入元素 7
    printf("在表中第 3 的位置插入新元素 7:\n");
    head = insertLine(head, 7, 3);
    display(head);
    //表中删除元素 2
    printf("删除元素 2:\n");
    head = delLine(head, 2);
    display(head);

    printf("元素 3 的位置是:%d\n", selectElem(head, 3));
    //表中第 3 个节点中的数据改为存储 6
    printf("将第 3 个节点存储的数据改为 6:\n");
    head = amendElem(head, 3, 6);
    display(head);
    return 0;
}
line* initLine(line * head) {
    
    
    int i = 0;
    line * list = NULL;
    head = (line*)malloc(sizeof(line));
    head->prior = NULL;
    head->next = NULL;
    head->data = 1;
    list = head;
    for (i = 2; i <= 3; i++) {
    
    
        line * body = (line*)malloc(sizeof(line));
        body->prior = NULL;
        body->next = NULL;
        body->data = i;

        list->next = body;
        body->prior = list;
        list = list->next;
    }
    return head;
}
line * insertLine(line * head, int data, int add) {
    
    
    //新建数据域为data的结点
    line * temp = (line*)malloc(sizeof(line));
    temp->data = data;
    temp->prior = NULL;
    temp->next = NULL;
    //插入到链表头,要特殊考虑
    if (add == 1) {
    
    
        temp->next = head;
        head->prior = temp;
        head = temp;
    }
    else {
    
    
        int i = 0;
        line * body = head;
        //找到要插入位置的前一个结点
        for (i = 1; i < add - 1; i++) {
    
    
            body = body->next;
            if (body == NULL) {
    
    
                printf("插入位置有误\n");
                break;
            }
        }
        if (body) {
    
    
            //判断条件为真,说明插入位置为链表尾
            if (body->next == NULL) {
    
    
                body->next = temp;
                temp->prior = body;
            }
            else {
    
    
                body->next->prior = temp;
                temp->next = body->next;
                body->next = temp;
                temp->prior = body;
            }
        }
    }
    return head;
}
line * delLine(line * head, int data) {
    
    
    line * temp = head;
    //遍历链表
    while (temp) {
    
    
        //判断当前结点中数据域和data是否相等,若相等,摘除该结点
        if (temp->data == data) {
    
    
            temp->prior->next = temp->next;
            temp->next->prior = temp->prior;
            free(temp);
            return head;
        }
        temp = temp->next;
    }
    printf("链表中无该数据元素\n");
    return head;
}
//head为原双链表,elem表示被查找元素
int selectElem(line * head, int elem) {
    
    
    //新建一个指针t,初始化为头指针 head
    line * t = head;
    int i = 1;
    while (t) {
    
    
        if (t->data == elem) {
    
    
            return i;
        }
        i++;
        t = t->next;
    }
    //程序执行至此处,表示查找失败
    return -1;
}
//更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值
line *amendElem(line * p, int add, int newElem) {
    
    
    int i = 0;
    line * temp = p;
    //遍历到被删除结点
    for (i = 1; i < add; i++) {
    
    
        temp = temp->next;
        if (temp == NULL) {
    
    
            printf("更改位置有误!\n");
            break;
        }
    }
    if (temp) {
    
    
        temp->data = newElem;
    }
    return p;
}
//输出链表的功能函数
void display(line * head) {
    
    
    line * temp = head;
    while (temp) {
    
    
        if (temp->next == NULL) {
    
    
            printf("%d\n", temp->data);
        }
        else {
    
    
            printf("%d->", temp->data);
        }
        temp = temp->next;
    }
}


猜你喜欢

转载自blog.csdn.net/weixin_44584479/article/details/114837496