数据结构之线性结构-链表

链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。

注意:malloc和free要成对出现,使用malloc申请的空间不会自动回收,要手动删除。

单链表

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

#define ElemType int//数据元素的类型

//定义单链表节点
typedef struct LinkNode {
    ElemType data;//数据域
    struct LinkNode* next;//指针域
}LinkNode, *LinkList;


//初始化,不带头结点
int initiList(LinkList L) {
    L = NULL;
    return 1;
}

//初始化,带头结点
int InitiList(LinkList &L) {
    L = (LinkList)malloc(sizeof(LinkNode));
    if (L == NULL) {
        return 0;
    }
    L->next = NULL;
    return 1;
}

//头插法建立单链表,带头结点,头结点为空,有时会用头节点存储
//如果不带头结点,每次插入节点后需要将它的地址赋给头指针L
LinkList List_HeadInsert(LinkList &L) {
    LinkNode* s;
    int x;
    L = (LinkList)malloc(sizeof(LinkNode));//为头节点分配空间
    L->next=NULL;//初始为空链表,防止指向其他位置
    scanf_s("%d", &x);//输入节点值
    while (x != 9999) {//定义终止条件。
    //这个方法不好,这会导致9999不能作为节点的数据元素。
        s = (LinkNode*)malloc(sizeof(LinkNode));//建立新节点
        s->data = x;
        s->next = L->next;
        L->next = s;
        scanf_s("%d", &x);
    }
    return L;
}

//尾插法建立单链表
LinkList List_TailInsert(LinkList& L) {
    int x;
    L = (LinkList)malloc(sizeof(LinkNode));//要先建立头结点,再进行下面的操作
    LinkNode* s, * r;
    r = L;
    scanf_s("%d", &x);
    while (x != 9999) {
        s = (LinkNode*)malloc(sizeof(LinkNode));//建立新节点
        s->data = x;
        r->next = s;
        r= s;
        scanf_s("%d", &x);
    }
    r->next = NULL;
    return L;
}

//按位查找
LinkNode *GetElem(LinkList L,int i){
    int j = 1;
    LinkNode* p = L->next;
    if (i == 0) {
        return L;
    }
    if (i < 1) {
        return NULL;
    }
    while (p && j < i) {
        p = p->next;
        j++;
    }
    return p;//若i>表长返回null,否则返回i个指针
}

//按值查找
LinkNode* LocateElme(LinkList L, ElemType e) {
    LinkNode* p = L->next;
    while (p!=NULL && p->data != e) {
        p = p->next;
    }
    return p;
}

//按位插入节点
//在第i个位置上插入e
//平均时间复杂度O(n)
int ListInsert(LinkList& L, int i, ElemType e) {
    if (i < 1)
        return 0;
    LinkNode* p;
    int j = 0;
    p = L;
    while (p != NULL && j < i - 1) {
        //找到第i-1个节点
        p = p->next;
        j++;
    }
    if (p == NULL)//p不合法
        return 0;
    LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));//建立一个节点
    s->data = e;
    s->next = p->next;
    p->next = s;//将节点防止p节点之后
    return 1;
}

//后插操作
//在节点p后插入e
int ListNextInsert(LinkNode* p, ElemType e) {
    if (p == NULL) {
        return 0;
    }
    LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
    if (s == NULL) {//内存分配失败
        return 0;
    }
    s->data = e;
    s->next = p->next;
    p->next = s;//将节点防止p节点之后
    return 1;
}

//


//按位删除
//删除第i个节点
int ListDelete(LinkList& L, int i, ElemType& e) {
    if (i < 1)
        return 0;
    LinkNode* p;
    int j = 0;
    p = L;
    while (p != NULL && j < i - 1) {
        //找到第i-1个节点
        p = p->next;
        j++;
    }
    if (p == NULL)//p不合法
        return 0;
    if (p->next == NULL)//第i个节点不存在
        return 0;

    LinkNode* q=p->next;//指向第i个节点
    e = q->data;//获取被删除节点元素值
    p->next = q->next;
    free(q);//释放内存空间
    return 1;
}

//删除指定节点
//如果p是最后一个节点会报错
int ListNodeDelete(LinkNode *p) {
    //交互q和它的后继节点,然后删除它的后继节点
    if (p == NULL)
        return 0;
    LinkNode* q = p->next;
    p->data = p->next->data;
    p->next = q->next;
    free(q);
    return 1;
}

//销毁链表
int DestroyList(LinkList& L) {
    LinkNode* p;
    LinkNode* s;
    int e = 1;
    p = L->next;
    free(L);
    while (p != NULL) {
        s = p;
        p = p->next;
        e = s->data;
        free(s);
    }
    return 1;
}


//求表长
int ListSize(LinkList L) {
    int length = 0;
    LinkNode* p;
    p = L->next;
    while (p != NULL) {
        //找到第i-1个节点
        p = p->next;
        length++;
    }
    return length;
}


//输出单链表
int PrintLink(LinkList L)
{
    LinkNode*p;
    p = L->next;
    printf("\n链表为;");
    while (p != NULL)
    {
        printf_s("%d ", p->data);
        p = p->next;
    }
    return 1;
}


int main()
{

//定义变量
    LinkList L;//声明一个单链表指针
    ElemType e;
    int length;
    L = NULL;
//建立链表
    //List_HeadInsert(L);
    List_TailInsert(L);
    PrintLink(L);
    length = ListSize(L);
    printf_s("\n链表长度为:%d", length);
//插入
    printf_s("\n在第二个位置上插入2\n");
    if (ListInsert(L, 2, 2)) {
        printf_s("插入成功");
    }
    else {
        printf_s("插入失败");
    }
    PrintLink(L);
//删除
    printf_s("\n删除第3个节点\n");
    if (ListDelete(L, 3, e)) {
        printf_s("%d删除成功",e);
    }
    else {
        printf_s("删除失败");
    }
    PrintLink(L);
    DestroyList(L);
    system("pause");
    return 0;
}


双链表

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

#define ElemType int//数据元素的类型

//定义链表节点
typedef struct DoubleNode {
    ElemType data;//数据域
    struct DoubleNode* next,* prior;//指针域
}DoubleNode, * DoubleLinkList;

int InitDLinkList(DoubleLinkList& L) {
    L = (DoubleNode*)malloc(sizeof(DoubleNode)); //分配-一个头结点
    if (L == NULL)//内存不足, 分配失败
        return 0;
    L->prior = NULL;//头结点的prior永远指向NULL
    L->next = NULL;//头结点之后暂时还没有节点
    return 1;
}

//建立节点
DoubleNode *creatDoubleNode(ElemType e) {
    DoubleNode* p;
    p = (DoubleNode*)malloc(sizeof(DoubleNode)); //分配-一个头结点
    if (p == NULL)//内存不足, 分配失败
        return NULL;
    p->data = e;
    p->prior = NULL;
    p->next = NULL;
    return p;
}



//在p结点之后插入s结点
int InsertNextDoubleNode(DoubleNode* p, DoubleNode* s) {
    if (p == NULL || s == NULL)
        return 0;
    s->next = p->next;//将结点*s插入到结点*p之后
    if(p->next!=NULL)//如果p有后继节点
        p->next->prior = s;
    s->prior = p;
    p->next = s;
    s->next = NULL;
    return 1;
}

//删除p的后继节点
int DeleteNextDoubleNode(DoubleNode* p) {
    if (p == NULL)//如果p不存在
        return 0;
    DoubleNode* q = p->next;
    if (q == NULL)//如果p没有后继节点
        return 0;
    p->next = q->next;
    if (q->next != NULL)//如果q不是最后一个节点
        q->next->prior = p;
    free(q);
    return 1;
}


//建立链表
int CreatDoubleLink(DoubleLinkList& L) {
    InitDLinkList(L);
    DoubleNode* s, * p;
    p = L;
    int i;
    for (i = 0; i < 7; i += 2) {
        s = creatDoubleNode(i);
        InsertNextDoubleNode(p, s);
        if (p->next == NULL)
            return 0;
        p = p->next;
    }
    return 1;
}

//输出链表
int PrintLink(DoubleLinkList L) {
    DoubleNode* p;
    p = L->next;
    printf("\n链表为:");
    while (p != NULL)
    {
        printf_s("%d ", p->data);
        p = p->next;
    }
    return 1;
}





int main() {
    DoubleLinkList L;
    CreatDoubleLink(L);
    PrintLink(L);

   
}

循环链表

循环单链表需要把最后一个节点的指针指向头结点

循环双链表不但要把最后一个节点的next指针指向头结点,还要把头结点的prior指针指向最后一个节点

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

#define ElemType int//数据元素的类型

typedef struct CircularListNode {
	ElemType data;
	struct CircularListNode* next;
}CircularListNode,*CircularList;

//初始化
int InitList(CircularList& L) {
	L = (CircularListNode*)malloc(sizeof(CircularListNode));
	if (L == NULL)//内存分配失败
		return 0;
	L->next = L;
	return 0;
}

//判空
bool isEmpty(CircularList L) {
	if (L->next == L)
		return true;
	else
		return false;
}

//判断表尾节点
bool isTail(CircularList L, CircularListNode* p) {
	if (p->next == L)
		return true;
	else
		return false;
}

//在p结点之后插入e结点
int InsertNextDoubleNode(CircularListNode* p, ElemType e) {
	if (p == NULL) {
		return 0;
	}
	CircularListNode* s = (CircularListNode*)malloc(sizeof(CircularListNode));
	if (s == NULL) {//内存分配失败
		return 0;
	}
	s->data = e;
	s->next = p->next;
	p->next = s;//将节点防止p节点之后
	return 1;
}




int main() {
	CircularList L;
	InitList(L);

}
posted @ 2021-06-27 21:31  Patrick-Rex  阅读(18)  评论(0)    收藏  举报  来源