编程必学之数据结构——链表(四)

C/C++
369
0
0
2022-07-25
标签   数据结构

上次小编写到双向线性链表了,还有双向循环链表以及数组链表没有写完,只是这两天太忙,就忘了更新,今天全部来补上。

1、双向循环链表

实现双向循环链表创建、插入、删除、释放内存等简单操作

#include <stdio.h>  
#include <stdlib.h>  
typedef struct Node
{
    int data;
    struct Node *pNext;
    struct Node *pPre;
}
NODE, *pNODE;
//创建双向循环链表  
pNODE CreateDbCcLinkList(void);
//打印链表  
void TraverseDbCcLinkList(pNODE pHead);
//判断链表是否为空  
int IsEmptyDbCcLinkList(pNODE pHead);
//计算链表的长度  
int GetLengthDbCcLinkList(pNODE pHead);
//向链表中插入节点  
int InsertEleDbCcLinkList(pNODE pHead, int pos, int data);
//从链表中删除节点  
int DeleteEleDbCcLinkList(pNODE pHead, int pos);
//删除整个链表,释放内存  
void FreeMemory(pNODE *ppHead);
int main(void){
    int flag = 0, length = 0;
    int position = 0, value = 0;
    pNODE head = NULL;
    head = CreateDbCcLinkList();
    flag = IsEmptyDbCcLinkList(head);
    if (flag)
        printf("双向循环链表为空!\n");
    else
    {
        length = GetLengthDbCcLinkList(head);
        printf("双向循环链表的长度为:%d\n", length);
        TraverseDbCcLinkList(head);
    }
    printf("请输入要插入节点的位置和元素值(两个数用空格隔开):");
    scanf("%d %d", &position, &value);
    flag = InsertEleDbCcLinkList(head, position, value);
    if (flag)
    {
        printf("插入节点成功!\n");
        TraverseDbCcLinkList(head);
    }
    else 
        printf("插入节点失败!\n");
    flag = IsEmptyDbCcLinkList(head);
    if (flag)
        printf("双向循环链表为空,不能进行删除操作!\n");
    else
    {
        printf("请输入要删除节点的位置:");
        scanf("%d", &position);
        flag = DeleteEleDbCcLinkList(head, position);
        if (flag)
        {
            printf("删除节点成功!\n");
            TraverseDbCcLinkList(head);
        }
        else 
            printf("删除节点失败!\n");
    }
    FreeMemory(&head);
    if (NULL == head)
        printf("已成功删除双向循环链表,释放内存完成!\n");
    else 
        printf("删除双向循环链表失败,释放内存未完成!\n");
    return 0;
}
//创建双向循环链表  
pNODE CreateDbCcLinkList(void){
    int i, length = 0, data = 0;
    pNODE p_new = NULL, pTail = NULL;
    pNODE pHead = (pNODE)malloc(sizeof(NODE));
    if (NULL == pHead)
    {
        printf("内存分配失败!\n");
        exit(EXIT_FAILURE);
    }
    pHead->data = 0;
    pHead->pNext = pHead;
    pHead->pPre = pHead;
    pTail = pHead;
    printf("请输入想要创建链表的长度:");
    scanf("%d", &length);
    for (i=1; i<length+1; i++)
    {
        p_new = (pNODE)malloc(sizeof(NODE));
        if (NULL == p_new)
        {
            printf("内存分配失败!\n");
            exit(EXIT_FAILURE);
        }
        printf("请输入第%d个节点元素值:", i);
        scanf("%d", &data);
        p_new->data = data;
        p_new->pPre = pTail;
        p_new->pNext = pHead;
        pTail->pNext = p_new;
        pHead->pPre = p_new;
        pTail = p_new;
    }
    return pHead;
}
//打印链表  
void TraverseDbCcLinkList(pNODE pHead){
    pNODE pt = pHead->pNext;
    printf("链表打印如:");
    while (pt != pHead)
    {
        printf("%d ", pt->data);
        pt = pt->pNext;
    }
    putchar('\n');
}
//判断链表是否为空  
int IsEmptyDbCcLinkList(pNODE pHead){
    pNODE pt = pHead->pNext;
    if (pt == pHead)
        return 1;
    else 
        return 0;
}
//计算链表的长度  
int GetLengthDbCcLinkList(pNODE pHead){
    int length = 0;
    pNODE pt = pHead->pNext;
    while (pt != pHead)
    {
        length++;
        pt = pt->pNext;
    }
    return length;
}
//向链表中插入节点  
int InsertEleDbCcLinkList(pNODE pHead, int pos, int data){
    pNODE p_new = NULL, pt = NULL;
    if (pos > 0 && pos < GetLengthDbCcLinkList(pHead) + 2)
    {
        p_new = (pNODE)malloc(sizeof(NODE));
        if (NULL == p_new)
        {
            printf("内存分配失败!\n");
            exit(EXIT_FAILURE);
        }
        while (1)
        {
            pos--;
            if (0 == pos)
            break;
            pHead = pHead->pNext;
        }
        p_new->data = data;
        pt = pHead->pNext;
        p_new->pNext = pt;
        p_new->pPre = pHead;
        pHead->pNext = p_new;
        pt->pPre = p_new;
        return 1;
    }
    else 
        return 0;
}
//从链表中删除节点  
int DeleteEleDbCcLinkList(pNODE pHead, int pos){
    pNODE pt = NULL;
    if (pos > 0 && pos < GetLengthDbCcLinkList(pHead) + 1)
    {
        while (1)
        {
            pos--;
            if (0 == pos)
            break;
            pHead = pHead->pNext;
        }
        pt = pHead->pNext->pNext;
        free(pHead->pNext);
        pHead->pNext = pt;
        pt->pPre = pHead;
        return 1;
    }
    else 
        return 0;
}
//删除整个链表,释放内存空间  
void FreeMemory(pNODE *ppHead){
    pNODE pt = NULL;
    while (*ppHead != NULL)
    {
        pt = (*ppHead)->pNext->pNext;
        if ((*ppHead)->pNext == *ppHead)
        {
            free(*ppHead);
            *ppHead = NULL;
        }
        else
        {
            free((*ppHead)->pNext);
            (*ppHead)->pNext = pt;
            pt->pPre = *ppHead;
        }
    }
}

输出结果:

请输入想要创建链表的长度:5

请输入第1个节点元素值:1

请输入第2个节点元素值:2

请输入第3个节点元素值:3

请输入第4个节点元素值:4

请输入第5个节点元素值:5

双向循环链表的长度为:5

链表打印如:1 2 3 4 5

请输入要插入节点的位置和元素值(两个数用空格隔开):2

6

插入节点成功!

链表打印如:1 6 2 3 4 5

请输入要删除节点的位置:2

删除节点成功!

链表打印如:1 2 3 4 5

已成功删除双向循环链表,释放内存完成!

2、双向循环链表

数据结构-线性表-链式存储-双向循环链表基本操作

#include <stdio.h>  
#include <stdlib.h>  
#define TRUE 1  
#define FALSE 0  
#define OK 1  
#define ERROR 0  
#define OVERFLOW -2  
typedef int Status;
typedef int ElemType;
/* 线性表的双向链表存储结构 */ 
typedef struct DuLNode
{
    ElemType data;
    struct DuLNode *prior,*next;
}
DuLNode,*DuLinkList;
/* 双链循环线性表的基本操作(14个) */ 
Status InitList(DuLinkList *L){ /* 产生空的双向循环链表L */
    *L=(DuLinkList)malloc(sizeof(DuLNode));
    if(*L)
    {
        (*L)->next=(*L)->prior=*L;
        return OK;
    }
    else 
        return OVERFLOW;
}
Status DestroyList(DuLinkList *L){ /* 操作结果:销毁双向循环链表L */
    DuLinkList q,p=(*L)->next; /* p指向第一个结点 */ 
    while(p!=*L) /* p没到表头 */
    {
        q=p->next;
        free(p);
        p=q;
    }
    free(*L);
    *L=NULL;
    return OK;
}
Status ClearList(DuLinkList L) /* 不改变L */{ /* 初始条件:L已存在。操作结果:将L重置为空表 */
    DuLinkList q,p=L->next; /* p指向第一个结点 */ 
    while(p!=L) /* p没到表头 */
    {
        q=p->next;
        free(p);
        p=q;
    }
    L->next=L->prior=L; /* 头结点的两个指针域均指向自身 */ 
    return OK;
}
Status ListEmpty(DuLinkList L){ /* 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */ 
    if(L->next==L&&L->prior==L)
        return TRUE;
    else 
        return FALSE;
}
int ListLength(DuLinkList L){ /* 初始条件:L已存在。操作结果:返回L中数据元素个数 */ 
    int i=0;
    DuLinkList p=L->next; /* p指向第一个结点 */ 
    while(p!=L) /* p没到表头 */
    {
        i++;
        p=p->next;
    }
    return i;
}
Status GetElem(DuLinkList L,int i,ElemType *e){ /* 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR */ 
    int j=1; /* j为计数器 */
    DuLinkList p=L->next; /* p指向第一个结点 */ 
    while(p!=L&&j<i) /* 顺指针向后查找,直到p指向第i个元素或p指向头结点 */
    {
        p=p->next;
        j++;
    }
    if(p==L||j>i) /* 第i个元素不存在 */ 
        return ERROR;
    *e=p->data; /* 取第i个元素 */ 
    return OK;
}
int LocateElem(DuLinkList L,ElemType e,Status(*compare)(ElemType,ElemType)){ /* 初始条件:L已存在,compare()是数据元素判定函数 */ 
    /* 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。 */ 
    /* 若这样的数据元素不存在,则返回值为0 */ 
    int i=0;
    DuLinkList p=L->next; /* p指向第1个元素 */ 
    while(p!=L)
    {
        i++;
        if(compare(p->data,e)) /* 找到这样的数据元素 */ 
            return i;
        p=p->next;
    }
    return 0;
}
Status PriorElem(DuLinkList L,ElemType cur_e,ElemType *pre_e){ /* 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱, */ 
    /* 否则操作失败,pre_e无定义 */
    DuLinkList p=L->next->next; /* p指向第2个元素 */ 
    while(p!=L) /* p没到表头 */
    {
        if(p->data==cur_e)
        {
            *pre_e=p->prior->data;
            return TRUE;
        }
        p=p->next;
    }
    return FALSE;
}
Status NextElem(DuLinkList L,ElemType cur_e,ElemType *next_e){ /* 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继, */ 
    /* 否则操作失败,next_e无定义 */
    DuLinkList p=L->next->next; /* p指向第2个元素 */ 
    while(p!=L) /* p没到表头 */
    {
        if(p->prior->data==cur_e)
        {
            *next_e=p->data;
            return TRUE;
        }
        p=p->next;
    }
    return FALSE;
}
DuLinkList GetElemP(DuLinkList L,int i) /* 另加 */{ /* 在双向链表L中返回第i个元素的位置指针(算法2.18、2.19要调用的函数) */ 
    int j;
    DuLinkList p=L;
    for(j=1;j<=i;j++)
        p=p->next;
    return p;
}
Status ListInsert(DuLinkList L,int i,ElemType e) /* 改进算法2.18 */{ /* 在带头结点的双链循环线性表L中第i个位置之前插入元素e,i的合法值为1≤i≤表长+1 */
    DuLinkList p,s;
    if(i<1||i>ListLength(L)+1) /* i值不合法 */ 
        return ERROR;
    p=GetElemP(L,i-1); /* 在L中确定第i-1个元素的位置指针p */ 
    if(!p) /* p=NULL,即第i-1个元素不存在 */ 
        return ERROR;
    s=(DuLinkList)malloc(sizeof(DuLNode));
    if(!s)
        return OVERFLOW;
    s->data=e; /* 在第i-1个元素之后插入 */
    s->prior=p;
    s->next=p->next;
    p->next->prior=s;
    p->next=s;
    return OK;
}
Status ListDelete(DuLinkList L,int i,ElemType *e) /* 算法2.19 */{ /* 删除带头结点的双链循环线性表L的第i个元素,i的合法值为1≤i≤表长+1 */
    DuLinkList p;
    if(i<1||i>ListLength(L)) /* i值不合法 */ 
        return ERROR;
    p=GetElemP(L,i); /* 在L中确定第i个元素的位置指针p */ 
    if(!p) /* p=NULL,即第i个元素不存在 */ 
        return ERROR;
    *e=p->data;
    p->prior->next=p->next;
    p->next->prior=p->prior;
    free(p);
    return OK;
}
void visit(ElemType c){
    printf("%d ",c);
}
void ListTraverse(DuLinkList L,void(*visit)(ElemType)){ /* 由双链循环线性表L的头结点出发,正序对每个数据元素调用函数visit() */
    DuLinkList p=L->next; /* p指向头结点 */ 
    while(p!=L)
    {
        visit(p->data);
        p=p->next;
    }
    printf("\n");
}
void ListTraverseBack(DuLinkList L,void(*visit)(ElemType)){ /* 由双链循环线性表L的头结点出发,逆序对每个数据元素调用函数visit()。另加 */
    DuLinkList p=L->prior; /* p指向尾结点 */ 
    while(p!=L)
    {
        visit(p->data);
        p=p->prior;
    }
    printf("\n");
}
Status compare(ElemType c1,ElemType c2){
    if(c1==c2)
    return TRUE;
    else 
    return FALSE;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    DuLinkList L;
    ElemType e;
    int j;
    Status i;
    i = InitList(&L); /* 初始化单循环链表L */ 
    printf("初始化双向循环链表L i=%d(1:初始化成功)\n", i);
    i=ListEmpty(L);
    printf("L是否空 i=%d(1:空 0:否)\n",i);
    ListInsert(L, 1, 15);
    ListInsert(L, 2, 25);
    ListInsert(L, 3, 35);
    ListInsert(L, 4, 45);
    i=GetElem(L,1,&e);
    j=ListLength(L);
    printf("L中数据元素个数=%d,第1个元素的值为%d。\n",j,e);
    printf("L中的数据元素依次为:");
    ListTraverse(L,visit);
    PriorElem(L,25,&e); /* 求元素5的前驱 */ 
    printf("25前面的元素的值为%d。\n",e);
    NextElem(L,35,&e); /* 求元素3的后继 */ 
    printf("35后面的元素的值为%d。\n",e);
    printf("L是否空 %d(1:空 0:否)\n",ListEmpty(L));
    j=LocateElem(L,45,compare);
    if(j)
        printf("L的第%d个元素为45。\n",j);
    else 
        printf("不存在值为45的元素\n");
    //删除第二个元素  
    ListDelete(L, 2, &e);
    printf("删除L的第2个元素:\n");
    if(i)
    {
        printf("删除的元素值为%d,现在L中的数据元素依次为:",e);
        ListTraverse(L,visit);
    }
    else 
        printf("删除不成功!\n");
    j=ListLength(L);
    printf("L中数据元素个数=%d\n",j);
    ListTraverse(L,visit);
    ListTraverseBack (L,visit);
    printf("清空L:%d(1: 成功)\n",ClearList(L));
    printf("清空L后,L是否空:%d(1:空 0:否)\n",ListEmpty(L));
    printf("销毁L:%d(1: 成功)\n",DestroyList(&L));
    return 0;
}

输出结果:

初始化双向循环链表L i=1(1:初始化成功)

L是否空 i=1(1:空 0:否)

L中数据元素个数=4,第1个元素的值为15

L中的数据元素依次为:15 25 35 45

25前面的元素的值为15

35后面的元素的值为45

L是否空 0(1:空 0:否)

L的第4个元素为45

删除L的第2个元素:

删除的元素值为25,现在L中的数据元素依次为:15 35 45

L中数据元素个数=3

15 35 45

45 35 15

清空L:1(1: 成功)

清空L后,L是否空:1(1:空 0:否)

销毁L:1(1: 成功)

3、双向循环链表总结 可以加小编的群466572167,交流学习,群内也有资料下载学习

每个节点除了存放元素数据之外,还需要保存指向下一个节点的指针,即所谓后指针,以及指向前一个节点的指针,即所谓前指针。

链表首节点的前指针和尾节点的后指针分别指向链表的尾节点和首节点。

编程必学之数据结构——链表(四)

加群466572167,可以交流学习

六、数组链表

这部分只做了解好了,使用数组链表效率不高。

1、数组链表

#include<iostream>  
using namespace std;
class List{
    private:
    int maxSize;
    int n;
    int *list;
    public:
    List(int max);
    ~List(){delete []list;}
    bool isEmpty(){return n==0;}
    int length(){return n;}
    int locate(int &x);//返回表中元素x的位置  
    bool retrieve(int k, int &x);//返回表中位置k,将之放入元素x  
    List& insert(int k,int x);//在位置k插入元素x  
    List& Delete(int k,int &x);//删除位置k的元素,将之存在x中]  
    void printList();
};
List::List(int max){
    maxSize = max;
    n = 0;
    list = new int[maxSize];
}
int List::locate(int &x){
    for(int i=0;i<n;i++)
        if(list[i]==x) return i;
            return -1;
}
bool List::retrieve(int k, int &x){
if(k<1||k>n) return false;
    x = list[k];
    return true;
}
List& List::insert(int k, int x){
    //if(k<0||k>n) 此处应抛出异常  
    //if(n==maxSize) 此处应抛出异常  
    for(int i = n-1; i >= k; i++)
        list[i+1] = list[i];
    list[k] = x;
    n++;
    return *this;
}
List& List::Delete(int k, int &x){
if(retrieve(k, x)){
    for(int i = k; i < n; i++)
        list[i] = list[i+1];
    n--;
    return *this;
}
//else 在此抛出异常
}
void List::printList() {
for(int i=0; i < n; i++)
    cout<<list[i]<<" ";
cout<<endl;
}
int main() {
    List list(10);
    list.insert(0,1);
    list.insert(1,2);
    list.insert(2,3);
    int listLength = list.length();
    list.printList();
    cout<<"数组链表的长度为"<<listLength<<endl;
    int delElement = 0;
    list.Delete(1, delElement);
    list.printList();
    cout<<"删除元素后数组链表的长度为"<<list.length()<<" "
    <<"删除的元素是"<<" "<<delElement<<endl;
    return 0;
}

输出结果:

1 2 3

数组链表的长度为3

1 3

删除元素后数组链表的长度为2 删除的元素是 2

2、数组链表总结 可以加小编的群466572167,交流学习,群内也有资料下载学习

(1)数组链表

链表中的每个元素都是数组,即由数组构成的链表

编程必学之数据结构——链表(四)

加群466572167,交流学习

(2)链表数组

数组中的每个元素都是链表,即由链表构成的数组

编程必学之数据结构——链表(四)

加群466572167,交流学习

(3)二维链表

链表中的每个元素都是链表,即由链表构成的链表

编程必学之数据结构——链表(四)

终于链表,这一系列写完了,有想要学习别的知识点的各位,可以在小编的文章下面留言评论,仅限C语言,C++方面的知识点哈