(C/C++)数据结构一:单链表、双向循环链表基本操作(详细注释)

c/c++伪代码形式:


//单链表基本操作


#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status
typedef char ElemType
//单链表

typedef struct  Lnode{
    ElemType data;
    struct Lnode*next;
}LNode,*LinkList;
//单链表重要操作

p = L;//p指向头结点
s = L->next;//s指向首元结点
p = p->next;//p指向下一结点

//初始化

Status InitList_L(LinkList L){
    L = (LinkList)malloc(sizeof(LNode));//L是头指针是头结点的地址
    L->next = NULL;//下面没有结点(无首元结点)
    return OK;
}
//取值:取单链表中第i个元素的内容
/*1、从第一个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p处置p=L->next
  2、j做计数器,累计当前扫描过的结点数,j初值为1
  3、当p指向扫描到的下一结点时,计数器j加1
  4、当j==i时,p所指的结点就是要找的第i个结点*/

Status GetElem_L(LinkList L, int i,ElemType &e){//获取线性表L中的某给数据元素的内容,通过e(引用)返回
    p = L->next;//初始化从首元元素触发
    j = 1;//计数器
    while(p&&j<i){//向后扫描,直到p指向第i个元素或p为空
        p = p->next;
        ++j;
    }
    if(!p||j>i)return ERROR;//第i个元素不存在
    e = p->data;
    return OK;
}
//查找:按值查找:根据指定数据获取该数据所在的位置(该数据的地址
      //按值查找:根据指定数据获取该数据所在的位置序号(时第几个数据元素)
/*1、从第一个结点起,依次和e相比较
  2、如果找到一个其值与e相等的数据元素,则返回其在链表中的“位置”或地址
  3、如果查遍整个链表都没有找到其值和e相等的元素,则返回0或“NULL”*/

LNode *LocateELem_L(LinkList L,Elemtype e){
    //找到,则返回L中值为e的数据元素的地址,查找失败返回NULL
    p = L->next;
    while(p&&p->data!=e)
        p = p ->next;
    return p;
}

int LocateElem_L(LinkList L,Elemtype e){
    //返回L中值为e的数据元素的位置序号,查找失败返回0
    p = L->next;
    j = 1;
    while(p&&p->data!=e){
        p = p->next;
        j++;
    }
    if(p) return j;
    else return 0;
}
//插入:在第i个结点前插入值为e的新结点
/*1、首先找到ai-1的存储位置p
  2、生成一个数据域为e的新结点s
  3、插入新结点:(1)新结点的指针域指向结点ai
                (2)结点ai-1的指针域指向新结点*/
//在L中第i个元素之前插入数据元素e
Status Listlnsert_L(LinkList *L,int i,ElemType e){
    p = L;//从头结点出发
    j = 0;//计数器
    while ((p&&j<i-1)){//寻找第i-1个结点,p指向i-1结点
        p = p->next;
        ++j;
    }
    if(!p||j>i-1)return ERROR;//i大于表长+1或者小于1,插入位置非法
    s = (LinkList)malloc(sizeof(LNode));//生成新节点s,将结点s的数据域置为e
    s->data =e;
    s -> next =  p -> next
    p -> next = s;
    return OK;
}
//删除:删除第i个结点
/*1、先找到ai-1的存储位置p,保存要删除的ai的值
  2、令p->next指向ai+1
  3、释放结点ai的空间*/

Status ListDelete_L(LinkList*L,int i,ElemType &e){
    p = L;
    j = 0;
    while(p->next&&j<i-1){
        p = p->next;
        ++j;//寻找第i个结点,并令p指向其前驱
    }
    if(!(p->next)||j>i-1)return ERROR;//删除位置不合理
    q = p->next //临时保存被删结点的地址以备释放
    p ->next = q ->next;//改变删除结点前驱结点的指针域p ->next = p ->next -》next
    e = q->data//保存删除结点的数据域
    free(q);//释放删除结点的空间
    return OK;
}
    
    


//销毁链表
Status DestroyList_L(LinkList L){
    LNode *p//或LinkList p;
    while(L){
        p = L;//清除p,L去找下一个,如果直接清除L就找不到下一个了
        L = L->next;
        free(p);
    }
}
//清空链表(销毁是毁坏整个链表,清空会保留链表(L存在)但元素为0
Status clearlist(LinkList L){//将L重置为空表
    LNode *p,*q;//或Linklist p,q
    p = L->next;
    while(p){//没到表尾
        q = p->next;//p,q两个哨兵,q去找p的下一个
        free(p);
        p = q 
    }
    L ->next = NULL;//头结点指针域为空
    return OK;
}
//求单链表表长

int ListLength_L(LinkList L){
    LNode *p;
    p = L->next//p指向首元结点
    int i = 0;
    while(p){
        i++;
        p = p->next;
    }
    return i;
}
//建立单链表:
//头插法——元素插入在链表头部,也叫前插法
/*1、从一个空表开始,重复读入数据;
  2、生成新结点,将读入数据存放到新结点的数据域中
  3、从最后一个结点开始,依次将各结点插入到链表的前端*/
void CreateList_H(LinkList L,int n){
    L = (LinkList)malloc(sizeof(LNode));
    L->next = NULL;//创建头结点
    for(i = n;i > 0;--i){
        p = (LinkList)malloc(sizeof(LNode));//建立第一个结点
        scanf("%d",&p->data);//插入结点
        p ->next = L ->next;//新结点指向头结点之后的结点
        L ->next = p;
    }
}
//尾插法——元素插入在链表尾部,也叫后插法
/*1、从一个空表L开始,将新结点逐个插入到链表的尾部,尾
     指针R指向链表的尾结点
  2、初始时,R同L均指向头结点,每读入一个数据元素则申请一个新结点,
     将新结点插入到尾结点后,R指向新结点*/
void CreateList_R(LinkList *L,int n){
    L = (LinkList)malloc(sizeof(LNode));
    LinkList *R = L;
    for(int i = 0;i < n;i++){
        p = (LinkList)malloc(sizeof(LNode));
        p ->next = NULL;
        R ->next = p;
        R = p;
    }
}
//双向链表
typedef struct DuLNode{
    Elemtype data;
    struct DuLNode *prior,*next;
}DuLNode,*DuLinkList;
//双向链表的插入

Status Listlnsert_DuL(DuLinkList &L,int i, ElemType e){
    if(!(p = GetElemP_DuL(L,i)))return ERROR;
    s = (DuLNode*)malloc(sizeof(DuLNode));
    s -> data = e;
    s -> prior = p -> prior;
    p -> prior ->next = s;
    s -> next = p;
    p -> prior = s; 
    return OK;
}

//双向链表的删除

void ListDelete_DuL(DuLink &L,int i,ElemType &e){
    //删除带头结点的双向循环链表L的第i个元素,并用e返回
    if(!(p= GetElemP_DuL(L,i)))return ERROR;
    e = p->data;//保存被删元素
    p -> prior -> next = p -> next;
    p -> next -> prior = p -> prior;
    free(p);
    return OK;
}

顺序表和链表的比较:

顺序表链表
空间存储空间预先分配,会导致空间闲置或溢出动态分配,不会出现空间闲置或溢出
存储密度不用为表示结点间的逻辑关系而增加额外的存储开销,存储密度等于1需要借助指针来体现元素间的逻辑关系,存储密度小于1
时间存取元素随机存取,按位置访问元素的时间复杂度为O(1)顺序存取,按位置访问元素时间复杂度为O(n)
插入、删除平均移动约表中一半元素,时间复杂度为O(n)不需移动元素,确定插入、删除位置后、时间复杂度为O(1)
适用情况

1、表长变化不大,且能事先确定变化的范围

2、很少进行插入或删除操作、经常按照元素位置序号访问数据元素

1、长度变化较大

2、频繁进行插入或删除操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用C++实现双向循环链表的代码,带有详细注释: ```c++ #include <iostream> // 定义双向循环链表节点结构体 struct Node { int data; // 存储数据 Node* prev; // 指向前一个节点的指针 Node* next; // 指向后一个节点的指针 }; // 定义双向循环链表类 class DoublyLinkedList { public: // 构造函数 DoublyLinkedList() { head = nullptr; // 初始化头指针为空 } // 在链表末尾插入节点 void insert(int data) { Node* newNode = new Node; // 创建新节点 newNode->data = data; // 设置新节点的数据 newNode->prev = nullptr; // 初始化新节点的前后指针为空 if (head == nullptr) { // 如果链表为空 head = newNode; // 将头指针指向新节点 head->next = head; // 新节点的后指针指向头节点形成循环 head->prev = head; // 新节点的前指针指向头节点形成循环 } else { // 如果链表不为空 Node* temp = head; // 创建临时指针指向头节点 while (temp->next != head) // 遍历链表找到最后一个节点 temp = temp->next; temp->next = newNode; // 将最后一个节点的后指针指向新节点 newNode->prev = temp; // 新节点的前指针指向最后一个节点 newNode->next = head; // 新节点的后指针指向头节点 head->prev = newNode; // 头节点的前指针指向新节点 } } // 在链表中删除节点 void remove(int data) { if (head == nullptr) // 如果链表为空 return; Node* temp = head; // 创建临时指针指向头节点 if (temp->data == data) { // 如果头节点就是要删除的节点 if (temp->next == head) { // 如果链表只有一个节点 head = nullptr; // 将头指针设为空 delete temp; // 删除节点 return; } else { // 如果链表有多个节点 head = temp->next; // 将头指针指向下一个节点 head->prev = temp->prev;// 下一个节点的前指针指向最后一个节点 temp->prev->next = head;// 最后一个节点的后指针指向下一个节点 delete temp; // 删除节点 return; } } while (temp->next != head) { // 遍历链表找到要删除的节点 if (temp->next->data == data) { Node* deleteNode = temp->next; // 创建临时指针指向要删除的节点 temp->next = deleteNode->next; // 将前一个节点的后指针指向要删除节点的下一个节点 deleteNode->next->prev = temp; // 将下一个节点的前指针指向要删除节点的前一个节点 delete deleteNode; // 删除节点 return; } temp = temp->next; // 指针后移 } } // 遍历链表并输出节点数据 void display() { if (head == nullptr) // 如果链表为空 return; Node* temp = head; // 创建临时指针指向头节点 std::cout << "List: "; while (temp->next != head) { // 遍历链表并输出节点数据 std::cout << temp->data << " "; temp = temp->next; // 指针后移 } std::cout << temp->data << std::endl; // 输出最后一个节点的数据 } private: Node* head; // 头指针 }; int main() { DoublyLinkedList list; // 创建双向循环链表对象 // 在链表末尾插入节点 list.insert(1); list.insert(2); list.insert(3); list.display(); // 输出链表 // 在链表中删除节点 list.remove(2); list.display(); // 输出链表 return 0; } ``` 上述代码实现了双向循环链表的插入、删除和遍历操作。其中,头指针指向链表的头节点,每个节点有一个前指针和一个后指针,指向前一个节点和后一个节点。在插入节点时,如果是第一个节点,则头指针指向该节点,该节点的前后指针都指向自身;如果不是第一个节点,则将该节点插入到链表的末尾。在删除节点时,先遍历链表找到要删除的节点,然后将该节点的前后指针分别指向下一个节点和上一个节点,最后删除该节点。在遍历链表时,需要注意链表为空的情况和最后一个节点的数据输出方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值