考研数据结构C语言-单链表的基本操作【带头结点】

单链表的基本操作【带头结点】

1.定义链表的数据结构

// 定义节点的数据结构
typedef struct LNode{
    
    
    int data;   // 数据域
    struct LNode *next;  // 指针域
} Node, *LinkList;

2.链表初始化

// 链表初始化并返回链表首地址
LinkList InitLinkList(){
    
    
    LinkList p;
    p = (LinkList)malloc(sizeof(Node));
    p->data = NULL;  // 头结点【指针指向头结点】
    p->next = NULL;
    return p;
}

3.插入元素

  • 头插法

    // 插入元素【头插法】
    bool PreheadInsertElement(LinkList &L,  int len){
          
          
        /*
        思路:始终在头指针后面插入节点
        */
        Node *s;
        int e;
        if(len < 1)
            return false;
        printf("输入数据:\n");
        for(int i=0; i < len; i++){
          
          
             s = (Node *)malloc(sizeof(Node));
             scanf("%d", &e);
             s->data = e;
             s->next = L->next;  // 头指针的下一个节点先赋值给新节点【这两步顺序不能乱】
             L->next = s;  // 再将头指针的下一个节点指向新节点
        }
        return true;
    }
    
  • 尾插法

    // 插入元素【尾插法】
    bool TailInsertElement(LinkList &L, int len){
          
          
        /*
        思路:头指针始终指向最后一个元素
        */
       LinkList head = L;  // 记录链表的首地址
       Node *s;
       int e;
       if(len < 1)
            return false;
       printf("请输入数据: \n");
       for(int i = 0; i<len; i++){
          
          
            s = (Node *)malloc(sizeof(Node));
            scanf("%d",  &e);
            s->data = e;  // 构造新节点
            s->next = NULL;  // 新节点的next指针域为空
            L->next = s;  // 连接新节点
            L = s; // 移动指针到新节点上
        }
        L = head;  // 重新将头指针指向最开始的首地址
        return true;
    }
    

4.插入节点

  • 指定位置之后插入节点

    // 指定位置之后插入节点
    bool InsertLocationBack(LinkList &L, int i, int e){
          
          
        /*
        思路: 找到要位置i
        链表: [NULL] ->[node1] ->[node2] ->[node3] |
        */
        if(i < 1 )  // 链表第一个位置(下标0)是头结点
            return false;
        int j = 0;  // 记录当前节点的位置【一开始是头结点】
        Node *s = L;  // 指针指向当前节点
        while (s != NULL && j < i) // 循环目的是找到要指定的位置
        {
          
          
            s = s->next;   // 变更指针指向
            j++;
        }
        if(s==NULL) // 位置i 非法
            return false;
        Node *N = (Node *)malloc(sizeof(Node));  // 定义节点
        if(N == NULL) //  内存分配失败
            return false;
        N->data = e;
        N->next = s->next;
        s->next = N;
        return true;
    }
    
  • 指定节点之前插入节点【未给链表头指针】

    // 指定节点之前插入节点【未给链表头指针】
    bool InsertLocationBefore(LinkList &p, int e){
          
          
        /*
        思路: 创建新节点与指定节点数据交换
        时间复杂度O(1)
        */
        if(!p)
            return false;
        Node *s;
        s = (Node *)malloc(sizeof(Node));
        if (!s)
            return false; //内存分配失败
        s->next = p->next;   // 将指定的节点赋值给新节点的指针next指针
        p->next = s;   // 将p的指针
        s->data = p->data;   // 交换数据
        p->data = e;
        return true;
    }
    
  • 指定位置之前插入数据【给了链表头指针】

    // 指定位置之前插入数据【给了链表头指针】
    bool InsertLocationBeforeWithHead(LinkList &L, int i, int e){
          
          
        /*
        思路: 找到i个的前一个位置
        时间复杂度O(n)
        */
       if(i < 1)
            return false;
        Node *s = L;
        Node *N;   // 定义新节点指针
        int j = 0;   // 记录当前节点的位置
        while (s !=NULL && j < i - 1)  // 找到第i个位置的前一个节点
        {
          
          
            s = s->next;
            j++;
        }
        if(s == NULL)   // 插入位置i非法
            return false;
        N = (Node *)malloc(sizeof(Node)); 
        if(!N)
            return false; // 申请内存地址失败
        N->data = e; 
        N->next = s->next;   // 指针交换
        s->next = N;
        return true;
    }
    

5.删除节点

// 删除指定位置的节点
bool DelLocationNode(LinkList &L, int i){
    
    
    /*
    思路: 找到要删除节点的前一个节点
    */
    if(i < 1)
        return false;
    int j = 0;  // 记录当前节点的位置
    Node *s = L;  // 指针指向当前节点
    while (s != NULL && j < i - 1) // 循环目的是找到要指定的位置的前一个节点
    {
    
    
        s = s->next;   // 变更指针指向
        j++;
    }
    if(!s)  // 当前节点是空
        return false;
    if(s->next == NULL)  //当前节点后已无节点
        return false;
    Node *p = s->next;  // p是要删除的节点
    s->next = p->next;
    free(p);  // 指向要删除的节点的内存地址空间释放
    return true;
}

6.删除指定节点

// 删除指定节点
bool DelNode(LinkList &p){
    
    
    /*
    思路:与删除节点的下一个节点交换数据,并将下一个节点删除【王道书上的有bug】
    */
   if(p == NULL){
    
    
        return false; // 节点为空
   };
   Node * s = p->next; // 要删除的节点的后一个节点
   if(s == NULL){
    
     // 要删除的节点为链表最后一个节点
        free(p); // 直接将p指向的节点内存空间释放
        p = NULL;
        return true;
   };
   int temp = s->data;   // 交换两个节点的数据
   s->data = p->data;
   p->data = temp;
   // 此时节点s 为要删除的节点了
   p->next = s->next;
   free(s);
   return true;
}

7.按位查找元素

// 按位查找元素
Node * GetElement(LinkList &L, int i){
    
    
    /*
    思路: 直接找到i个位置的节点将指针返回
    */
    if(i < 1)
        return NULL;
    Node * p = L;
    int j = 0;  // 当前节点从头结点开始
    while (p != NULL && j < i)   
    {
    
    
        p = p->next;
        j++;
    }
    if(p == NULL)
        return NULL;  // 位置非法【这里讲课视频也有bug, 存在索引超出范围也是查找不到的直接返空】
    return p;
}

8.按值查找

// 按值查找第一个值为e的节点位置
int GetLocation(LinkList &L, int e){
    
    
    /*
    思路:指针不为空一直往后找,直到找到元素为止或者等指针为空的时候跳出循环
    */
   Node *p = L;
   int j = 0;
   while (p != NULL)
   {
    
        
        if(p->data != e){
    
    
            p = p->next;
            j++;
        }else{
    
    
            break;
        }
   }
   if(p == NULL)  // 指针为空的时候表示没有该元素返回-1
        return -1;
   return j;
}

9.求表长度

// 求表长度
int Tablelength(LinkList L){
    
    
    int length = 0;
    Node * p = L;
    while( p != NULL){
    
    
        p = p->next;
        length ++;
    }
    return length;
}

10.打印链表数据

// 打印链表数据
void PrintElement(LinkList &L){
    Node *p;
    p = L->next;  // 从第1个位置才开始有数据
    printf("数据打印: \n");
    while (p != NULL)
    {
        printf("%d\n", p->data);
        p = p->next;
    }
}

7.main 函数调用

// main函数
int main(){
    
    
    LinkList L = InitLinkList();
    // PreheadInsertElement(L, 4);  // 头插法
    TailInsertElement(L,2);  // 尾插法
    PrintElement(L);  // 打印链表
    InsertLocationBack(L, 2, 666);  // 按位插入
    PrintElement(L);  //打印
    DelLocationNode(L, 2);  // 删除指定位置的元素
    PrintElement(L); // 打印元素
    InsertLocationBefore(L->next->next->next, 1000);   //第三个节点之前插入1000【不传入头指针】
    PrintElement(L);
    InsertLocationBeforeWithHead(L, 3, 8888);  // 在第三个位置之前插入8888【传入头指针】
    PrintElement(L);
    Node * item = GetElement(L, 9);  // 按位查找元素
    if(item != NULL){
    
    
        printf("按位查找的元素:%d\n", item->data);
    }else{
    
    
        printf("查找失败!位置不合法或者超出索引\n");

    }
    int value = GetLocation(L, 8888);  //  按值查找
    printf("按值查找位置:%d\n", value);
    PrintElement(L);
    int length = Tablelength(L);  // 打印表长度
    printf("表长度为: %d\n", length); 
    DelNode(L->next);  // 指定节点删除【这里删除位置1的节点】
    PrintElement(L);
    DelNode(L->next);  // 指定节点删除【这里删除位置1的节点】
    PrintElement(L);
    DelNode(L->next);  // 指定节点删除【这里删除位置1的节点】
    PrintElement(L);
    system("pause");
    return 0;
}

完整代码

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#define InitSize 5
/*
单链表-基本操作【带头结点】
*/

// 定义节点的数据结构
typedef struct LNode{
    
    
    int data;   // 数据域
    struct LNode *next;  // 指针域
} Node, *LinkList;


// 链表初始化
LinkList InitLinkList(){
    
    
    LinkList p;
    p = (LinkList)malloc(sizeof(Node));
    p->data = NULL;  // 头结点【指针指向头结点】
    p->next = NULL;
    return p;
}

// 插入元素【头插法】
bool PreheadInsertElement(LinkList &L,  int len){
    
    
    /*
    思路:始终在头指针后面插入节点
    */
    Node *s;
    int e;
    if(len < 1)
        return false;
    printf("输入数据:\n");
    for(int i=0; i < len; i++){
    
    
         s = (Node *)malloc(sizeof(Node));
         scanf("%d", &e);
         s->data = e;
         s->next = L->next;  // 头指针的下一个节点先赋值给新节点【这两步顺序不能乱】
         L->next = s;  // 再将头指针的下一个节点指向新节点
    }
    return true;
}

// 插入元素【尾插法】
bool TailInsertElement(LinkList &L, int len){
    
    
    /*
    思路:头指针始终指向最后一个元素
    */
   LinkList head = L;  // 记录链表的首地址
   Node *s;
   int e;
   if(len < 1)
        return false;
   printf("请输入数据: \n");
   for(int i = 0; i<len; i++){
    
    
        s = (Node *)malloc(sizeof(Node));
        scanf("%d",  &e);
        s->data = e;  // 构造新节点
        s->next = NULL;  // 新节点的next指针域为空
        L->next = s;  // 连接新节点
        L = s; // 移动指针到新节点上
    }
    L = head;  // 重新将头指针指向最开始的首地址
    return true;
}

// 指定位置之后插入节点
bool InsertLocationBack(LinkList &L, int i, int e){
    
    
    /*
    思路: 找到要位置i
    链表: [NULL] ->[node1] ->[node2] ->[node3] |
    */
    if(i < 1 )  // 链表第一个位置(下标0)是头结点
        return false;
    int j = 0;  // 记录当前节点的位置【一开始是头结点】
    Node *s = L;  // 指针指向当前节点
    while (s != NULL && j < i) // 循环目的是找到要指定的位置
    {
    
    
        s = s->next;   // 变更指针指向
        j++;
    }
    if(s==NULL) // 位置i 非法
        return false;
    Node *N = (Node *)malloc(sizeof(Node));  // 定义节点
    if(N == NULL) //  内存分配失败
        return false;
    N->data = e;
    N->next = s->next;
    s->next = N;
    return true;
}

// 指定节点之前插入节点【未给链表头指针】
bool InsertLocationBefore(LinkList &p, int e){
    
    
    /*
    思路: 创建新节点与指定节点数据交换
    时间复杂度O(1)
    */
    if(!p)
        return false;
    Node *s;
    s = (Node *)malloc(sizeof(Node));
    if (!s)
        return false; //内存分配失败
    s->next = p->next;   // 将指定的节点赋值给新节点的指针next指针
    p->next = s;   // 将p的指针
    s->data = p->data;   // 交换数据
    p->data = e;
    return true;
}

// 指定位置之前插入数据【给了链表头指针】
bool InsertLocationBeforeWithHead(LinkList &L, int i, int e){
    
    
    /*
    思路: 找到i个的前一个位置
    时间复杂度O(n)
    */
   if(i < 1)
        return false;
    Node *s = L;
    Node *N;   // 定义新节点指针
    int j = 0;   // 记录当前节点的位置
    while (s !=NULL && j < i - 1)  // 找到第i个位置的前一个节点
    {
    
    
        s = s->next;
        j++;
    }
    if(s == NULL)   // 插入位置i非法
        return false;
    N = (Node *)malloc(sizeof(Node)); 
    if(!N)
        return false; // 申请内存地址失败
    N->data = e; 
    N->next = s->next;   // 指针交换
    s->next = N;
    return true;
}

// 删除指定位置的节点
bool DelLocationNode(LinkList &L, int i){
    
    
    /*
    思路: 找到要删除节点的前一个节点
    */
    if(i < 1)
        return false;
    int j = 0;  // 记录当前节点的位置
    Node *s = L;  // 指针指向当前节点
    while (s != NULL && j < i - 1) // 循环目的是找到要指定的位置的前一个节点
    {
    
    
        s = s->next;   // 变更指针指向
        j++;
    }
    if(!s)  // 当前节点是空
        return false;
    if(s->next == NULL)  //当前节点后已无节点
        return false;
    Node *p = s->next;  // p是要删除的节点
    s->next = p->next;
    free(p);  // 指向要删除的节点的内存地址空间释放
    return true;
}

// 删除指定节点
bool DelNode(LinkList &p){
    
    
    /*
    思路:与删除节点的下一个节点交换数据,并将下一个节点删除【王道书上的有bug】
    */
   if(p == NULL){
    
    
        return false; // 节点为空
   };
   Node * s = p->next; // 要删除的节点的后一个节点
   if(s == NULL){
    
     // 要删除的节点为链表最后一个节点
        free(p); // 直接将p指向的节点内存空间释放
        p = NULL;
        return true;
   };
   int temp = s->data;   // 交换两个节点的数据
   s->data = p->data;
   p->data = temp;
   // 此时节点s 为要删除的节点了
   p->next = s->next;
   free(s);
   return true;
}

// 按位查找元素
Node * GetElement(LinkList &L, int i){
    
    
    /*
    思路: 直接找到i个位置的节点将指针返回
    */
    if(i < 1)
        return NULL;
    Node * p = L;
    int j = 0;  // 当前节点从头结点开始
    while (p != NULL && j < i)   
    {
    
    
        p = p->next;
        j++;
    }
    if(p == NULL)
        return NULL;  // 位置非法【这里讲课视频也有bug, 存在索引超出范围也是查找不到的】
    return p;
}

// 按值查找第一个值为e的节点位置
int GetLocation(LinkList &L, int e){
    
    
    /*
    思路:指针不为空一直往后找,直到找到元素为止或者等指针为空的时候跳出循环
    */
   Node *p = L;
   int j = 0;
   while (p != NULL)
   {
    
        
        if(p->data != e){
    
    
            p = p->next;
            j++;
        }else{
    
    
            break;
        }
   }
   if(p == NULL)  // 指针为空的时候表示没有该元素返回-1
        return -1;
   return j;
}

// 打印链表数据
void PrintElement(LinkList &L){
    
    
    Node *p;
    p = L->next;  // 从第1个位置才开始有数据
    printf("数据打印: \n");
    while (p != NULL)
    {
    
    
        printf("%d\n", p->data);
        p = p->next;
    }
}

// 求表长度
int Tablelength(LinkList L){
    
    
    int length = 0;
    Node * p = L;
    while( p != NULL){
    
    
        p = p->next;
        length ++;
    }
    return length;
}

// main函数
int main(){
    
    
    LinkList L = InitLinkList();
    // PreheadInsertElement(L, 4);  // 头插法
    TailInsertElement(L,2);  // 尾插法
    PrintElement(L);  // 打印链表
    InsertLocationBack(L, 2, 666);  // 按位插入
    PrintElement(L);  //打印
    DelLocationNode(L, 2);  // 删除指定位置的元素
    PrintElement(L); // 打印元素
    InsertLocationBefore(L->next->next->next, 1000);   //第三个节点之前插入1000【不传入头指针】
    PrintElement(L);
    InsertLocationBeforeWithHead(L, 3, 8888);  // 在第三个位置之前插入8888【传入头指针】
    PrintElement(L);
    Node * item = GetElement(L, 9);  // 按位查找元素
    if(item != NULL){
    
    
        printf("按位查找的元素:%d\n", item->data);
    }else{
    
    
        printf("查找失败!位置不合法或者超出索引\n");

    }
    int value = GetLocation(L, 8888);  //  按值查找
    printf("按值查找位置:%d\n", value);
    PrintElement(L);
    int length = Tablelength(L);  // 打印表长度
    printf("表长度为: %d\n", length); 
    DelNode(L->next);  // 指定节点删除【这里删除位置1的节点】
    PrintElement(L);
    DelNode(L->next);  // 指定节点删除【这里删除位置1的节点】
    PrintElement(L);
    DelNode(L->next);  // 指定节点删除【这里删除位置1的节点】
    PrintElement(L);
    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_49501453/article/details/129092099