C语言练习——数据结构

目录

1、单向链表基础:创建节点、头插法新增节点、遍历打印全部数据

2、链表尾插法,输入数字持续追加到链表末尾

3、根据值删除链表中指定节点

4、链表反转,输出反转后的链表

5、链表求长度、查找指定数值节点

[6、数组栈、链表栈两种方式实现:push 入栈、pop 出栈](#6、数组栈、链表栈两种方式实现:push 入栈、pop 出栈)


1、单向链表基础:创建节点、头插法新增节点、遍历打印全部数据

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;

// 链表结点定义
typedef struct LNode
{
  ElemType data;
  struct LNode *next;
} LNode, *LinkList;

// 初始化链表,带头结点
bool InitList(LinkList &L)
{
  L = (LNode *)malloc(sizeof(LNode));
  if (L == NULL)
    return false;
  L->next = NULL;
  return true;
}

// 头插法:在链表头部插入元素(头结点之后)
bool HeadInsert(LinkList &L, ElemType val)
{
  // 新建结点s
  LNode *s = (LNode *)malloc(sizeof(LNode));
  if (s == NULL)
    return false;
  s->data = val;// 新结点数据赋值

  //新节点指向前一个节点的next,即下一个节点的data段,然后前一个节点的next再指向新节点的data段
  s->next = L->next; // 新结点先接住后面第一个数据结点
  L->next = s;       // 头结点指向新结点
  return true;
}

// 遍历打印所有有效结点
void PrintList(LinkList L)
{
  LNode *p = L->next; // p跳过头结点,指向第一个数据节点
  while (p != NULL)
  {
    printf("%d ", p->data);
    p = p->next;
  }
  printf("\n");
}

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

  HeadInsert(L, 10);
  HeadInsert(L, 20);
  HeadInsert(L, 30);

  printf("链表内容:");
  PrintList(L);
  // 输出:30 20 10

  return 0;
}

2、链表尾插法,输入数字持续追加到链表末尾

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;

// 链表结点定义
typedef struct LNode
{
  ElemType data;
  struct LNode *next;
} LNode, *LinkList;

// 初始化链表,带头结点
bool InitList(LinkList &L)
{
  L = (LNode *)malloc(sizeof(LNode));
  if (L == NULL)
    return false;
  L->next = NULL;
  return true;
}

// 尾插法:在链表尾部插入元素(头结点之后)
bool TailInsert(LinkList &L, ElemType val)
{
  // 新建结点s
  LNode *s = (LNode *)malloc(sizeof(LNode));
  if (s == NULL)
    return false;

  s->data = val;  // 新结点数据赋值
  s->next = NULL; // 尾节点后面没有元素,置空

  // 遍历找到链表最后一个节点
  LNode *p = L;           // p指向头结点,跳过头结点,指向第一个数据节点
  while (p->next != NULL) // 遍历找到链表最后一个节点
  {
    p = p->next;
  }
  p->next = s; // 尾部节点指向新节点
  return true;
}

// 遍历打印所有有效结点
void PrintList(LinkList L)
{
  LNode *p = L->next; // p跳过头结点,指向第一个数据节点
  while (p != NULL)
  {
    printf("%d ", p->data);
    p = p->next;
  }
  printf("\n");
}

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

  TailInsert(L, 10);
  TailInsert(L, 20);
  TailInsert(L, 30);

  printf("链表内容:");
  PrintList(L);
  // 输出:10 20 30

  return 0;
}

3、根据值删除链表中指定节点

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int ElemType;

typedef struct LNode
{
  ElemType data;
  struct LNode *next;
} LNode, *LinkList;

// 初始化链表,带头结点
bool InitList(LinkList &L)
{
  L = (LNode *)malloc(sizeof(LNode));
  L->next = NULL;
  return L != NULL;
}

// 尾插法
bool TailInsert(LinkList &L, ElemType val)
{
  LNode *s = (LNode *)malloc(sizeof(LNode));
  s->data = val;
  s->next = NULL;

  LNode *p = L;
  while (p->next != NULL)
    p = p->next;
  p->next = s;
  return true;
}

// 根据值删除第一个匹配节点
bool DeleteByValue(LinkList &L, ElemType val)
{
  LNode *p = L, *del;
  // p = L:p从头结点开始遍历(保存【前驱节点】)
  // del:临时指针,用来存放要被删除的节点

  // 条件1:p->next != NULL  后面还有节点,没遍历完
  // 条件2:p->next->data != val 下一个节点不是目标值
  while (p->next != NULL && p->next->data != val)
    p = p->next; // p向后移动

  // 退出循环两种情况:
  // 情况1:p->next == NULL → 整条链表走完,找不到值val
  if (p->next == NULL)
    return false; // 删除失败

  // 情况2:找到了!p->next 就是要删除的节点
  del = p->next;       // del指向待删除节点
  p->next = del->next; // 前驱节点直接跳过目标节点,连上后面节点(链表断链移除目标)
  free(del);           // 释放被删除节点的堆内存,防止内存泄漏
  return true;         // 删除成功
}

// 打印链表
void PrintList(LinkList L)
{
  LNode *p = L->next;
  while (p != NULL)
  {
    printf("%d ", p->data);
    p = p->next;
  }
  printf("\n");
}

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

  // 快速构建链表 10 20 30
  TailInsert(L, 10);
  TailInsert(L, 20);
  TailInsert(L, 30);

  printf("原链表:");
  PrintList(L);

  DeleteByValue(L, 20);
  printf("删除20后:");
  PrintList(L);

  return 0;
}
// 原链表:10 20 30
// 删除20后:10 30

4、链表反转,输出反转后的链表

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int ElemType;

typedef struct LNode
{
  ElemType data;
  struct LNode *next;
} LNode, *LinkList;

// 初始化链表,带头结点
bool InitList(LinkList &L)
{
  L = (LNode *)malloc(sizeof(LNode));
  L->next = NULL;
  return L != NULL;
}

// 尾插法
bool TailInsert(LinkList &L, ElemType val)
{
  LNode *s = (LNode *)malloc(sizeof(LNode));
  s->data = val;
  s->next = NULL;

  LNode *p = L;
  while (p->next != NULL)
    p = p->next;
  p->next = s;
  return true;
}

// 链表反转
void ReverseList(LinkList &L)
{
  // 边界判断:空链表 / 只有1个数据节点,无需反转,直接退出
  if (L->next == NULL || L->next->next == NULL)
    return;

  LNode *pre = NULL;        // 前驱节点
  LNode *cur = L->next;     // cur指向第一个有效数据节点(10)
  LNode *next;              // 临时保存下一个节点,防止断链

  while (cur != NULL)       // cur遍历到NULL结束
  {
    next = cur->next;       // ①先存好后面节点地址!防止断链
    cur->next = pre;        // ②当前节点反向,指向前驱
    pre = cur;              // ③pre往前走一步
    cur = next;             // ④cur往前走一步
  }
  // 循环结束:pre指向原链表最后一个节点(30)
  L->next = pre;            // 头结点接入新的第一个节点
}

// 打印链表
void PrintList(LinkList L)
{
  LNode *p = L->next;
  while (p != NULL)
  {
    printf("%d ", p->data);
    p = p->next;
  }
  printf("\n");
}

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

  TailInsert(L, 10);
  TailInsert(L, 20);
  TailInsert(L, 30);

  printf("原链表:");
  PrintList(L);

  ReverseList(L);
  printf("反转链表后:");
  PrintList(L);

  return 0;
}

5、链表求长度、查找指定数值节点

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;

typedef struct LNode
{
  ElemType data;
  struct LNode *next;
} LNode, *LinkList;

// 1. 获取链表长度
int ListLength(LinkList L)
{
  int len = 0;
  LNode *p = L;
  while (p->next != NULL)
  {
    len++;
    p = p->next;
  }
  return len;
}

// 2. 按值查找,找到返回结点指针;找不到返回NULL
LNode *LocateElem(LinkList L, ElemType val)
{
  LNode *p = L->next;
  while (p != NULL && p->data != val)
  {
    p = p->next;
  }
  return p;
}

// 初始化带头结点单链表
LinkList InitList()
{
  LinkList L = (LNode *)malloc(sizeof(LNode));
  L->next = NULL;
  return L;
}

// 尾插
void TailInsert(LinkList L, ElemType val)
{
  LNode *s = (LNode *)malloc(sizeof(LNode));
  s->data = val;
  s->next = NULL;
  LNode *p = L;
  while (p->next != NULL)
    p = p->next;
  p->next = s;
}

int main()
{
  LinkList L = InitList();
  TailInsert(L, 10);
  TailInsert(L, 20);
  TailInsert(L, 30);

  printf("链表长度 = %d\n", ListLength(L));

  ElemType target = 20;
  LNode *res = LocateElem(L, target);
  if (res != NULL)
    printf("找到数值%d\n", res->data);
  else
    printf("未找到\n");

  return 0;
}

//链表长度 = 3
//找到数值20

6、数组栈、链表栈两种方式实现:push 入栈、pop 出栈

数组栈

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
typedef int ElemType;//重命名

// 顺序栈定义
typedef struct
{
  ElemType data[MAXSIZE];
  int top;
} SqStack;

// 初始化
void InitSqStack(SqStack *s)
{
  s->top = -1;
}

// 入栈
int Push(SqStack *s, ElemType val)
{
  if (s->top >= MAXSIZE - 1)
  {
    printf("栈满!\n");
    return 0;
  }
  s->data[++s->top] = val;
  return 1;
}

// 出栈
int Pop(SqStack *s, ElemType *res)
{
  if (s->top == -1)
  {
    printf("栈空!\n");
    return 0;
  }
  *res = s->data[s->top--];
  return 1;
}

int main()
{
  SqStack st;
  InitSqStack(&st);

  Push(&st, 10);
  Push(&st, 20);
  Push(&st, 30);

  ElemType num;
  Pop(&st, &num);
  printf("出栈元素:%d\n", num);
  Pop(&st, &num);
  printf("出栈元素:%d\n", num);
  Pop(&st, &num);
  printf("出栈元素:%d\n", num);
  Pop(&st, &num);

  return 0;
}

链表栈

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;//重命名

//定义结点
typedef struct StackNode{
    ElemType data;
    struct StackNode *next;
}StackNode, *LinkStack;

//初始化:创建头结点
void InitLinkStack(LinkStack *S){
    *S = (StackNode*)malloc(sizeof(StackNode));
    (*S)->next = NULL;  //头结点后继置空
}

//入栈(头插,头结点后面是栈顶)
int LinkPush(LinkStack S, ElemType val){
    StackNode *p = (StackNode*)malloc(sizeof(StackNode));
    if(p == NULL) return 0;
    p->data = val;
    p->next = S->next;
    S->next = p;
    return 1;
}

//出栈
int LinkPop(LinkStack S, ElemType *res){
    //判断栈空:头结点后面没有元素
    if(S->next == NULL){
        printf("栈空!\n");
        return 0;
    }
    StackNode *p = S->next;   //p指向栈顶结点
    *res = p->data;
    S->next = p->next;
    free(p);
    return 1;
}

int main()
{
    LinkStack S;
    InitLinkStack(&S);

    LinkPush(S, 11);
    LinkPush(S, 22);
    LinkPush(S, 33);

    ElemType val;
    LinkPop(S, &val);
    printf("出栈:%d\n", val);
    LinkPop(S, &val);
    printf("出栈:%d\n", val);
    LinkPop(S, &val);
    printf("出栈:%d\n", val);
    LinkPop(S, &val);

    return 0;
}
对比项目 顺序栈(数组栈) 链栈【带头结点】
结构核心 数组 + top 下标 头结点 (无数据),head->next 为栈顶
空栈条件 top = -1 head->next == NULL
栈满条件 top == MAXSIZE-1,会溢出 无容量上限
Push 入栈逻辑 1. 检查栈满2.++top3.data[top]=值 1. 新建结点 p2.p->next = head->next3.head->next = p(头插)
Pop 出栈逻辑 1. 检查栈空2. 取出data[top]3.top-- 1. 检查栈空 head->next==NULL2.p 指向栈顶结点 p=head->next3. 取值4.head->next = p->next5.free(p)
内存行为 只移动下标,数组数据残留 动态 malloc/free,结点销毁
时间复杂度 O(1) O(1)
相关推荐
ShuiShenHuoLe1 小时前
Go html/template 使用入门
开发语言·golang·html
geovindu1 小时前
java: Gale-Shapley Algorithm
java·开发语言·后端·算法
冻柠檬飞冰走茶2 小时前
PTA基础编程题目集 7-34 通讯录的录入与显示(C语言实现)
c语言·开发语言·数据结构·算法
zander2582 小时前
LeetCode 79. 单词搜索
算法·深度优先
星核0penstarry2 小时前
DeepSeek-V4-Flash 正式公测:大模型行业进入「极速平价普惠时代」
java·开发语言·人工智能
老洋葱Mr_Onion2 小时前
【C++】高精度模板
开发语言·c++·算法
脚踏实地皮皮晨2 小时前
003003002_WPF Grid 基类官方类定义逐行深度解析
开发语言·windows·算法·c#·wpf·visual studio
xieliyu.2 小时前
Java 线程池入门详解:线程池基础定义 + ThreadPoolExecutor 全参构造方法逐参数解析
java·开发语言·idea·javaee
一条泥憨鱼2 小时前
【从0开始学习计算机网络】| HTTP方法疑点解析
开发语言·计算机网络·http