c语言数据结构

article/2025/9/15 16:17:31

目录

 

一、数据结构构造概述

1.1、什么是数据结构

1.2、数据的逻辑结构的4种分类

二、线性表   

2.1、线性表概述

2.2、顺序表

2.3、链表

2.3.1、链表节点的创建

2.3.2、链表结点遍历

2.3.3、链表结点删除

2.3.4、链表的插入

三、栈和队列

3.1、栈概述

3.2、栈的基本运算

3.3、顺序栈的类型定义

3.4、链式栈的类型定义

3.5、队列

3.5.1、队列的常用运算

3.5.2、顺序队列类型定义

3.5.3、链队列类型定义

四、二叉树

五、查找方法

5.1、顺序查找

5.2、折半查找

六、排序方法

6.1、冒泡排序

6.2、选择排序

6.3、插入排序法

七、综合应用  -- 反转链表


一、数据结构构造概述

1.1、什么是数据结构

数据结构是计算机存储,管理数据的方式。数据必须依据某种逻辑联系组织在一起存储在计算机内,数据结构研究的就是这种数据的存储结构和数据的逻辑结构

1.2、数据的逻辑结构的4种分类

 

二、线性表   

2.1、线性表概述

  • 线性结构是简单而且常用的数据结构  ,而线性表则是一种典型的线性结构
  • 存储数据,最简单,最有效的方法是把它们存储在一个线性表中。
  • 一个线性表是n个元素的有限序列。每个元素在不同的情况下有不同的含义,可以是整数,也可以是字符。
  • 线性表的存储方式有两种:顺序存储方式链表存储方式
  • 线性表示例

color = {red,orange,yellow,green,blue,black,white}; // color线性表的组成

score = {90,84,38,29,90}    

2.2、顺序表

  • 就是把线性表中的所有元素按照其逻辑顺序依次存储到指定位置开始的一块连续的存储区域。
  • 线性表中的第1个元素的存储位置就是指定的存储位置,第i个元素的存储位置紧接第i-1个元素的存储位置的后面。

顺序表的特点:

  • 在顺序表中,各元素的逻辑顺序跟物理顺序一致,第i项就存在第i个位置。
  • 对顺序表中的所有元素,既可以顺序访问,也可以随机访问。
#include "stdio.h"
#include "stdlib.h"
#define MAXLISTSIZE 1024  // 宏定义顺序表中最大容量
/*
typedef struct的作用
1、简化较复杂的声明
2、typedef并没有创建一个新的类型,而是为某个已经存在的类型增加一个新的名字,如示例中的linearlist是 struct的别名typedef struct    // 此处省略了结构的的名称 为匿名结构体   typedef struct line{int id;char name;}linearlist
使用与不使用typedef的区别
1、使用typedef声明结构体,可以省略struct这个关键字
linearlist list[3];2、不使用typedef声明结构,则不可以省略struct这个关键字
struct linearlist list[3];*/typedef struct            // 结构体        
{int data[MAXLISTSIZE]; // 顺序表int last;              // 顺序表元素个数
}Linearlist;             // linearlist 结构体别名void ListList(Linearlist *list)      // 打印线性顺序表    声明一个指针 list 指向结构体linearlist首地址
{int i;                             //   定义变量iprintf("当前线性表的状态\n");      //   提示语句
if(list -> last == 0)                 //  条件判断  顺序表为空
printf("当前线性表为空\n");           //  如果添加成立 则输出当前语句else                                 //  如果条件不成立 则遍历数组for(i=0;i<(list->last);i++)     //  循环遍历顺序表printf("[%4d]",list -> data[i]); // 输出元素printf("\n");                      // 打印一个换行符}void Output(Linearlist *list)   // 打印说明文档
{system("cls");               // 清理屏幕printf("-       顺序表       -\n");printf("-      a:追加一个节点 i:插入一个节点       -\n");printf("-      d:删除一个节点 e:退出     -\n ");ListList(list);              // 打印线性顺序表
} Linearlist *CreateList()    // 创建线性顺序表
{
Linearlist *list = (Linearlist*)malloc(sizeof(Linearlist)); // 分配空间
list ->last =0;       // 初始化节点值
return list;          // 返回初始化头节点指针
}void AppendNode(Linearlist *list,int n)
{if(list->last<MAXLISTSIZE)  // 顺序表不溢出{list ->data[list->last] = n; // 初始化节点值list ->last += 1;}}void insertNode(Linearlist *list,int n,int pos)   //插入人节点{int j;
if(pos < 0 || pos > list -> last)  // || 逻辑或运算  | 位运算 或  只要一个pos<0 pos>list->last中一个成立就行printf("所插入的位置超出顺序表的范围\n");else{for(j=list->last;j>=pos;j--)           // 逆向遍历顺序表list->data[j+1] = list ->data[j];  // 元素后移list->data[pos]=n;                 // 指向节点赋值 list->last++;                      // 顺序表长度加1}}void DeleteNode(Linearlist*list,int pos)  // 删除节点{int j;if((pos<0)|| (pos>list->last))       // 删除位置超出顺序表的范围printf("所要删除的位置超出顺序表的范围\n");else{for(j=pos;j<list->last;j++)         //  遍历顺序表list->data[j]=list->data[j+1];  //  元素前移list->last--;                       //  顺序长度减一}}int main()   // 主函数{ int key,pos; // 整型变量char ch;     // 字符型变量
Linearlist *list; // list = CreateList();  // 创建顺序列表while(1){Output(list);printf("请选择:"); ch=getchar();        // 接受选项fflush(stdin);       // 清除缓存  if(ch=='a')          // 追加{printf("请输入要追加的数据");scanf("%d",&key);AppendNode(list,key);}else if(ch=='i')    // 插入{ printf("请输入要插入的数据的位置:");scanf("%d",&pos);printf("请输入要插入的数据");scanf("%d",&key);insertNode(list,key,pos);}else if(ch=='d')       // 删除{printf("请输入要删除的数据的位置");scanf("%d",&pos);DeleteNode(list,pos);}else if(ch=='e')  // 退出exit(0);Output(list);fflush(stdin);   // 清除缓存}return 0;}

2.3、链表

单链表是一种简单的链表表示,也叫做线性链表,用它来表示线性表时,用指针表示结点间的逻辑关系,因此单链表的一个存储结点包含两部分

  • data部分称为数据域,用于存储线性表的一个数据元素;
  • link部分称为指针域,用于存放一个指针,该指针指示链表下一个结点的开始存储地址

单链表的第一个结点也称为首结点,它的地址可以通过链表的头指针head找到,其他结点的地址通过前驱结点的link域名找到,链表的最后一个结点没有后继,则在link域放一个空指针NULL,图中用“^”表示

线性表中的数据元素的顺序与器链表表示中的物理顺序可能不一致,一般通过但链表的指针将各个数据元素按照线性表的逻辑顺序链接起来。当head为空时,则单链表为空,否则为非空表。

2.3.1、链表节点的创建

链表是一种动态的数据结构,在程序中需要使用malloc()和free()函数创建链表。

为有效的地存储节点数据,并且实现链表的链式功能,可建立linknode结构体

struct linknode{int data;   // 结点数据linknode *next; // 结点指针,指向下一个结点};

运行上面的结构体声明后,linknode就成为一个动态指针结构。建立了结点的结构后,接下来定义一个结构体指针变量,该指针变量在使用前必须分配存储空间,然后以用户键入初值初始化结点数据,同时初始化该结点指向的下一个结点为空

linknode *ptr;ptr=(linknode *)malloc(sizeof(linknode)); // 获取linknode在内存中所占的字节的长度,用malloc函数开辟出保存linknode所占内存的一段地址,并强制转换为linknode类型指针//sizeof函数计算数据(包括数组、变量、类型、结构体等)所占内存空间,用字节数表示/*malloc函数用于在内存开辟了一段地址,而这段地址的首地址存在返回的那个指针变量里,由于不知道到底这段地址有多长,可以存什么变量,所以它的类型是空的*/

输入到数据的结构中

scanf("%d",&ptr->data)

pro->next=unll

2.3.2、链表结点遍历

链表的遍历和数组的遍历相似,数组的使用下标,而链表则是通过指针处理每一个结点的遍历。不同的是,数组可以随机访问数组元素,链表结构不一定要用遍历的方式访问其结点,如果要访问第n个结点的内容,就一定要遍历到n-1个结点才能够知道结果在哪里

2.3.3、链表结点删除

链表结点删除的三种情况

从动态的内存操作理论来说,应该在删除第一个结点后,立即将删除的结点内存释放给系统,如果ptr是指向删除结点的指针,那么释放命令如下

free(ptr);

如果是整个链表结构,除了使用上述命令,还需要借助于链表的遍历方法。

2.3.4、链表的插入

#include <stdio.h>  // 头文件  stdio 就是指 “standard input & output"(标准输入输出)
#include <stdlib.h> /* stdlib.h里面定义了五种类型、一些宏和通用工具函数,常用的函数如malloc()、calloc()、realloc()、free()、system()、atoi()、atol()、rand()、srand()、exit()*/
struct linknode      // 链表结构声明
{           int data;           // 存储结点数据struct linknode *next;  // 指向下一个结点
};
typedef struct linknode LinkNode;  // 为结构体定义新类型/*链表函数*/
LinkNode *CreatLinkNode()          // 创建链表函数
{int i;                // 整型变量iLinkNode *head,*ptr,*p;   // 链表结点  head指向链表的首地址 head=null或head=0时 链表为空head = (LinkNode *)malloc(sizeof(LinkNode));  // 分配内存if(!head)                     // 判断条件{printf("内存分配失败!\n");    // 打印提示语句  exit(1);                      // 退出}printf("请输入第1个数据:");scanf("%d",&head->data);      // 创建结点内容head ->next=NULL;ptr=head;                     // ptr指向链表开始for(i=1;i<5;i++)              // 循环创建结点{p=(LinkNode *)malloc(sizeof(LinkNode));if(!p){printf("内存分配失败!\n");exit(1);}printf("请输入第%d个数据:",i+1);scanf("%d",&p->data);p->next=NULL;ptr->next=p;      // 链接结点ptr=ptr->next;    // 指向下一个结点}return head;
}/*链表的遍历*/LinkNode *FindNode(LinkNode *head,int num)  // {LinkNode *ptr;ptr=head;             // 指向链表起始while(ptr!=NULL)      // 遍历链表{if(ptr->data==num) return ptr;  // 查找编号ptr=ptr->next;}return ptr;}/*链表结点的插入模块*/LinkNode *insertNode(LinkNode *head,LinkNode *ptr,int value) // {LinkNode * newnode=(LinkNode *)malloc(sizeof(LinkNode));  // 分配内存if(!newnode) return NULL;newnode->data=value; // 创建结点内容newnode->next=NULL;  // 设置指针初值if(ptr==NULL){newnode->next=head;return newnode;}else{if(ptr->next == NULL) ptr->next=newnode;// 是否是链表结束else{newnode ->next=ptr->next;  // 新结点指向下一个结点ptr->next=newnode;}}return head;}/* 链表结点删除*/LinkNode *DeleteNode(LinkNode *head,LinkNode *ptr) //  定义自定义类型LinkNode的两个形参指针head,ptr 指向 LinkNode结构体{LinkNode *pre;   // 指向前一个结点if(ptr=head)     // 是否用链表的开始return head->next;  // 输出第2个结点else{pre=head;while(pre->next!=ptr)pre=pre->next;if(ptr->next=NULL)       // 是否是链表结束pre->next=NULL;      // 最后一个结点elsepre->next=ptr->next;  // 中间结点}free(ptr);             // 释放结点内存return head;}/*链表输出模块*/void PrintNode(LinkNode *ptr)   {while(ptr!=NULL)   // 指针ptr非空{printf("%d\t",ptr->data);  // 输出当前指针指向结点的数据ptr=ptr->next;             // 指针向后移动指向下一个结点}printf("\n"); // 打印一个换行符}/*链表的内存释放*/void FreeLinkNode(LinkNode *head) // 定义LinkNode类型的head指针指向结构体LinkNode的首地址{LinkNode *ptr;while(head!=NULL){ptr=head;head=head->next;free(ptr);}}int main(){int num,value;LinkNode *head,*ptr;      // 定义结构体自定义数据类型LinkNode的指针 head ptr head = CreatLinkNode();PrintNode(head);printf("输入要查找的数据:\n");scanf("%d",&num);ptr=FindNode(head,num); // 查询数据if(!ptr)printf("没有找到\n");else{printf("找到啦!\n请输入要插入的数据:\n");scanf("%d",&value);head=insertNode(head,ptr,value);// 插入数据PrintNode(head);}printf("请输入要查找并删除的数据:\n");scanf("%d",&num);ptr=FindNode(head,num);if(!ptr)printf("没有找到\n");else{printf("找到了!\n");head=DeleteNode(head,ptr);  // 删除PrintNode(head);}FreeLinkNode(head);return 0;}

三、栈和队列

3.1、栈概述

栈是一种重要的线性结构,它是受限的线性表,它仅能在表的一端进行插入和删除运算的线性表,栈被广泛地应用到各种系统的程序设计中。

  • 通常的插入,删除的这一端为栈顶(Top),另外一端为栈底(Bottom)
  • 当表中没有元素时称为空栈
  • 栈为后进先出(Last In First Out)的线性表,简称为LIFO表

栈的修改是按照先进先出的原则,每次删除的总是当前栈中“最新”的元素,即最后插入(进栈)的元素,而最先插入的原则是被放在栈的底部,要到最后才能删除。

3.2、栈的基本运算

  • InitStack(s);构造一个空栈s。
  • StackEmpty(s):判断空。若s为空栈,则返回TRUE。
  • StackFull(s);  判断满。如s为满栈,则返回TRUE。
  • Push(S,x):进栈,如栈s不满,则将元素x插入s的栈顶。
  • Pop(s):退栈。若栈s非空,则将s的栈顶元素删去,并返回该元素。
  • StackTop(s);取栈顶元素,若栈s非空,则返回栈顶元素,但不改变栈的状态。

3.3、顺序栈的类型定义

顺序栈的定义和顺序表的定义一样,通常我们使用结构体定义顺序栈,记录栈顶坐标从而实现各种操作。

#define StackSize 100   // 假定预分配的栈空间最多为100个元素
typedef char DataType;  // 假定栈元素的数据类型为字符
typedef struct
{DataType data[StackSize];     // 定义栈数组int top;                      // 栈顶
} seqstack;                      // 结构体别名

栈底位置是固定不变的,可设置在向量两端的任意一个端点。

栈顶位置是随着进栈和退栈的操作而变化的,用一个整型量top(通常称为top)

3.4、链式栈的类型定义

栈的链式存储结构称为链栈。链栈是没有附加结点的运算受限的单链表。栈顶指针就是链表的头指针。跟单链表一样,通常我们使用结构体实现链式栈的功能,结构体内一个量存储结点值,一个量存储指针,实现链式结构,就像单链表有指针一样,我们也为链式栈定义头结点,以便对链式栈进行操作。

#include "stdio.h"
typedef struct stacknode     // 链式栈结构
{DataType data            // 栈元素Struct stacknode *next   // 结构体自用}StackNode;
typedef struct
{StackNode *top;   // 栈顶指针
}LinkStack;/* 注意LinkStack结构体类型的定义为了在方便函数体中修改top指针本身;若要记录栈中元素的个数,可将元素的属性放在LinkStack类型中定义。*/

3.5、队列

队列(Queue)是一种线性结构,它是一种受限的线性表,它只允许在一端进行插入,而在另外一端进行删除的运算。受限的线性队列的修改也是依先进先出的原则进行的。

3.5.1、队列的常用运算

  • InitQueue(Q);置空队。构造一个空队列Q。
  • QueueEmpty(Q);判队空。若队列Q空,则返回真值,否则返回假值。
  • QueueFull(Q);判队满。若队列Q为满,则返回真值,否则返回假值。
  • EnQueue(Q,x);若队列Q非满,则将元素x插入Q的队尾。此操作简称入队。
  • DeQueue(Q);若对列Q非空,则删去Q的对头元素,并返回该元素,此操作简称出队。
  • QueueFront(Q);若队列Q非空,则返回对头元素,但不改变队列Q的状态。

3.5.2、顺序队列类型定义

  • 队列的顺序存储结构称为顺序队列,顺序队列实际上是运算受限的顺序表。
  • 同顺序表一样,顺序队列用一个向量空间来存放当前队列中的元素。
  • 由于队列的队头和队尾的位置是变化的,设置两个指针front和rear分别指示对头元素和队尾元素在向量空间中的位置,它们的初值在队列初始化时均应置0,

  • 入队时:将新元素插入rear所指的位置,然后加1
  • 出队时:删去front所指的元素,然后将front加1并返回被删除的元素
  • 注意:当头尾指针相等时,队列为空;在非空队列里,队头指针始终指向队头元素,尾指针始终指向对尾元素的下一个位置。

3.5.3、链队列类型定义

队列的链式存储结构简称为链队列。

它是限制仅在表头删除和表尾插入的单链表。

注意:增加指向链表上的最后一个结点的尾指针,便于在表尾进行插入操作,图中的Q为LinKQueue型的指针

 

四、二叉树

  • 树形结构是一类重要的非线性结构。
  • 树形结构的结点之间有分支,并具有层次关系的结构。
  • 在程序中,用树来表示源程序的语法结构
  • 在数据库系统中,可以用树来组织信息
  • 二叉树是树形结构的一个重要类型。
  • 二叉树(BinaryTree)是N(N>=0)个结点的有限集,它或者是空集(N=0),或者由一个根结点及两颗互不橡胶的分别称作这个根的左子树和右子树的二叉树组成,二叉树可以是空集;根可以有空的左子树或右子树,或者左,右子树皆为空。

  • 由上图可知,一颗非空的二叉树由根结点及左,右子树等3个部分组成。
  • 因此,在任一结点上,可以按某种次序进行3个操作
  1. 访问结点本身(N);
  2. 遍历该结点的左子树(L)
  3. 遍历该结点的右子树(R)
  • 该三种操作有6种执行次序:NLR、LNR、LRN、NRL、RNL、RLN
  • 前三种和后三种的执行次序是对称的,常用的为前序遍历(NLR),中序遍历(LNR),后序遍历(LRN)

举例

  • 前序遍历:A-B-D-Y-E-C-F-X-Z
  • 中序遍历:  D-Y-B-E-A-F-C-Z-X
  • 后序遍历:  Y-D-E-B-F-Z-X-C-A

五、查找方法

5.1、顺序查找

  • 核心思想:同数组遍历一样,就是从数组的第一元素开始,检查数组的每一个元素,以便确定是否有查找的数据。
  • 注意:因为从头检查到尾,因此数组数据是否排序就不再重要。
#include "stdio.h"
#include "stdlib.h"
#include "time.h"
#define MAX 100    // 宏定义   最大数组容量struct element{    // 记录结构声明int key;
};
typedef struct element record;  // 结构体别名
record data[MAX+1];             // 结构体别名2// 顺序查找
int seqseqrch(int key)
{int pos;                  // 数组索引data[MAX].key=key;        // 将seqseqrch函数的形参key传递给结构体element中的keypos=0;                    //  数组索引从0开始(数组从头开始找)/*while(1)的作用while(1);   while(1){}    条件成立时,通过break跳出死循环    写在单片机中防止程序跑飞两者都是定义死循环(无限循环)*/while(1)                  // 定义死循环{if(key==data[pos].key) // 是否找到break;             // 上面条件满足时,跳出死循环pos++;                 // 条件不满足时 继续查找下一个元素}if(pos==MAX)              // 数组从头开始找==数组容量最大   说明在最后   /*return 1,return 0 ,return -10与1   0表示假  1表示真-1与0   -1表示语句执行不成共        0表示语句执行成功*/return -1;                // 一般的return -1;表示出错    return 0;表示正确elsereturn pos;           // 返回pos的值
}/*主程序*/
int main()
{int checked[300];   // 检查数组int i,temp;         // 定义整型变量long temptime;      //  长整型变量srand(time(&temptime)%60);  // 使用时间初始随机数for(i=0;i<300;i++)checked[i]=0;           // 清除检查数组i=0;while(i!=MAX)      // 生成数组值的循环{temp =rand()%300;// 随机数范围0~299if(checked[temp]==0)  // 是否已有的值{data[i].key=temp;checked[temp]=1;  // 设置此值生成过i++;}}while(1)  // 定义为死循环可以一直查找数据{printf("请输入查找的值0~299\n"); // 提示语句scanf("%d",&temp);  // 导入查找的值if(temp!=-1)        // temp =rand()%300;  键入的数值不能为负数{i=seqseqrch(temp);  // 调用顺序查找if(i!=-1)           // 数值索引没有赋值printf("找到查找值%d\n",temp);elseprintf("没有找到查找值%d\n",temp);}elseexit(1);  // 结束程序}return 0;
}

5.2、折半查找

如果查找的数据已经排序,可以使顺序查找的方式进行查找,但更好的查找方法是折半查找法

折半查找的核心思想:将数据分区,首先折半查找检查中间元素,如果中间元素小于查找的关键值,可以确定数据是存储在前半段,否则就在后半段。然后继续在可能存在的半段数据内重复上述操作,直到找到或已经没有数据可以分区,表示没有找到。

将数组分为上、下范围,用low和high分别表示,则中间元素为(low+high)/2。在进行查找时,可以分为以下3种情况。

  • 如果查找关键值小于数组的中间元素,关键值在数据数组的前半部分。、
  • 如果查找的关键值小于数组的中间元素,关键值在数据数组的后半部分。
  • 如果查找关键值等于数组的中间元素,中间元素就是查找的值。
#include "stdio.h"
#include "stdlib.h"
#define MAX 21   // 最大struct element{    int key;
};typedef struct element record; // 结构体别名record data[MAX]={1,3,5,7,9,21,25,33,46,89,100,121,127,144,234,432,500,509,689,700,890};// 折半查找int binarysearch(int key)   // 定义折半查找函数binarysearch 形参key 为要查找的元素{int low,high,mid;         // 数组开始、结束,中间变量low=0;                    // 数组从0开始,标记查找下限high=MAX-1;               //数组结束,标记查找上限  下标从0开始  所以数组下标最大值为MAX-1while(low<=high)          //因为数组已经排序 所以前半段中的数组元素必须小于等于后半段中的数组元素 low<=hign{mid=(low+high)/2;        // 折半查找中间值if(key<data[mid].key)    // 当待查找的数据小于折半中间值high=mid-1;          // 在折半的前一半重新查找elseif(key>data[mid].key) // 当带查找数据大于折半中间值low=mid+1;        // 在折半的后一半重新查找return  mid;             // 找到了返回下标}return -1;                   // 没有找到返回-1	
}int main(){int found;     // 是否找到变量int value;     // 查找值while(1)       // 死循环{printf("请输入查找值0~1000\n");  // 提示语句scanf("%d",&value);            // 键盘输入要查找的值if(value!=-1)                  // 查找的值不为负数{found=binarysearch(value);  // 调用折半查找if(found!=-1)printf("找到查找值:%d[%d]\n",value,found);elseprintf("没有找到查找值:%d\n",value);}elseexit(1); // 结束程序}return 0;}

六、排序方法

6.1、冒泡排序

冒泡排序的核心思想:将较小的元素搬到数组的开始,将较大的元素慢慢地浮到数组的最后。冒泡排序法使用交换方式进行排序。

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define MAX 20void bubble(char *arr,int count)  // 定义一个冒泡函数  有两个形参  char类型的指针 arr 整型count
{int i,j;char temp;for(j=count;j>1;j--)  // 外循环控制比较轮数{for(i=0;i<j-1;i++) // 内循环控制每轮比较的次数{if(arr[i+1]<arr[i]) // 比较相邻元素{temp =arr[i+1];arr[i+1]= arr[i];arr[i]=temp;}}printf("输出结果[%s]\n",arr); // 交换后输出字符串}
}
int main()
{char array[MAX];  // 定义一个char类型的数组 数组长度为20int count;printf("请输入排序的字符串:\n");gets(array);             // 存储字符数组count=strlen(array);     // 测试字符数组bubble(array,count);return 0;
}

6.2、选择排序

核心思想:选择排序法是从排序的元素中宣传最小的一个元素和第一元素交换位置,然后从剩下的元素中选出最小的元素和第二个元素交换,重复这种处理的方法,直到最后一个元素为止。

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define MAX 20
// 选择排序法
void select(char *arr,int count)
{int pos;int i,j;char temp;for(i=0;i<count-1;i++) // 数组{pos=i;temp=arr[pos];for(j=i+1;j<count;j++){if(arr[j]<temp){pos=j;temp=arr[j];}}arr[pos]=arr[i]; /*交换当前字符和最小字符两个元素和小标*/arr[i]=temp;printf("输出结果:[%s]\n",arr); // 交换后输出}
}int main()
{char array[MAX];int count;printf("输入将排序的字符串:\n");gets(array);count=strlen(array);select(array,count);return 0;
}

6.3、插入排序法

插入排序法是对排序元素的前两个元素的排序,然后将第三个元素插入已经排好序的两个元素中,所以这三个元素任然是从小到大排序,接着第四个元素插入,重复操作直到所有的元素都排好序。

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define MAX 10    // 宏定义
// 插入排序法
void insert(char *arr,int count)  // 插入排序函数  两个形参   指向char类型数组的指针   数组长度
{int i,j;      // 定义变量i循环次数,j数组长度char temp;     // 定义变量tempfor(i=1;i<count;i++)  // 遍历数组{             temp=arr[i];        // 创建初值   当前数组值存入临时变量tempj=i-1;              // 开始位置while(j>=0 && temp<arr[j])  // 循环元素后移{arr[j+1]=arr[j];j--;}arr[j+1]=temp;     // 插入字符 printf("输出结果:[%s]\n",arr);}}int main()
{char array[MAX];int count;printf("输入将排序的字符串");gets(array);/*strlen所作的是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符'\0'为止,然后返回计数器值(长度不包含'\0')*/count=strlen(array); // 返回array的数组长度insert(array,count); // 调用函数return 0;
}

七、综合应用  -- 反转链表

#include "stdio.h"
#include "stdlib.h"
// 链表结构声明
struct linknode{
int data;
struct linknode *next; // 结构体自用
};
// 定义新类型
typedef struct linknode LinkNode;
// 创建链表
LinkNode *CreatLinkNode(int *array,int len)
{LinkNode *head;  // 链表首结点LinkNode *ptr,*p;int i;head=(LinkNode*)malloc(sizeof(LinkNode)); // 分配内存if(!head)                                 // 检查指针
/*if(head)printf("xxxx");这里是判断head是否为真,如果是真,则执行printf("xxxx");head不等于0为真,head等于0为假。
if(!head)printf("xxxx");这里是判断head是否为假,如果是假,则执行printf("xxxx");head不等于0,
!head为假,head等于0,!head为真。*/return NULL;head->data=array[0];head->next=NULL;    // 设置指针初值ptr=head;           //ptr指向链表开始for(i=1;i<len;i++){p=(LinkNode*)malloc(sizeof(LinkNode));if(!p)return NULL;p->data=array[i];p->data=NULL;ptr->next=p;  // 连接结点 p与ptr关联ptr=ptr->next;//指向下一个结点}return head;   // 返回首地址
}// 链表反转
LinkNode *invertNode(LinkNode *head)
{LinkNode *mid,*last;mid=NULL;while(head!=NULL){last = mid;       // last 是head的后结点mid=head;head=head->next; // 下一个结点mid->next=last;  // mid指向后结点last}return mid;
}// 链表输出
void PrintNode(LinkNode *ptr)
{while(ptr!=NULL)                // 链表遍历循环{printf("%d\t",ptr->data);    // 输出结点数据ptr=ptr->next;               // 指向下一个结点}printf("\n");
}// 链表的内存释放
void FreeLinkNode(LinkNode *head)
{LinkNode *ptr;while(head!=NULL){ptr =head;head=head->next;free(ptr);}
}int main()
{int list[6] = {1,2,3,4,5,6};LinkNode *head;head=CreatLinkNode(list,6);if(!head){printf("内存分配失败");exit(1);}printf("原来的链表是:\n");PrintNode(head);head=invertNode(head);printf("反转后的链表是:\n");PrintNode(head);FreeLinkNode(head);return 0;}

 


http://chatgpt.dhexx.cn/article/bfJsJLOo.shtml

相关文章

C语言数据结构知识点小结(全)

Catologue C语言数据结构一、基本概念和术语二、时间、空间复杂度&#xff08;1&#xff09;时间复杂度&#xff08;2&#xff09;空间复杂度 三、类C语言有关操作补充1&#xff1a;数组定义补充2&#xff1a;动态内存分配补充3&#xff1a;C中的参数传递 四、线性表&#xff0…

七丶青龙nvjdc部署教程+短信验证登录对接傻妞

青龙nvjdc部署教程短信验证登录对接傻妞Nolanjdc 没有服务器的先自行购买&#xff0c;这里推荐腾讯云2H4G8M首年70–点击购买 青龙面板安装教程 傻妞机器人安装教程 XDD安装教程 QQ交流&#xff1a;1014549449 --------------点击跳转 注丶只能对接一个&#xff0c;要么对接…

手机短信验证

在项目中经常会用到手机短信验证注册&#xff0c;登录等功能&#xff0c;所以我想写一篇文章来给大家提供一个参考。 阿里大于-个人感觉比较好用的短信验证平台&#xff0c;下面是接入阿里大于sdk的步骤。 阿里大于官网&#xff1a;直通车, 进入官网需要注册&#xff0c;注册…

014_关于session实现短信验证登录的前端启动

014_关于session实现短信验证登录的前端启动 1、进入到nginx相对应的文件夹&#xff0c;shfit右键&#xff0c;进入PowerShell并且执行nginx 2、启动我们的nginx,嘿嘿&#xff0c;可以访问我们的前端网页啦&#xff01;&#xff01;&#xff01;它就是模仿我们的大众点评来着…

基于Redis的短信验证登录

基于Redis的短信验证登录 1、用户调用发送短信验证码接口2、用户调用登录/注册接口3、用户调用校验接口4、SpringMvc拦截器注册5、token刷新拦截器6、登录拦截器 1、用户调用发送短信验证码接口 用户调用sendCode()接口&#xff0c;把phone传到后端&#xff0c;后端对phone进行…

使用聚合数据短信API测试(短信验证登录)

搞一手聚合数据短信API测试&#xff08;之前用阿里云的搞过&#xff0c;今天我们用聚合&#xff09; 注册聚合账号&#xff01;聚合官网链接登陆后进入短信服务API&#xff08;免费提供十次&#xff09; 添加自定义模板&#xff08;审核速度看脸&#xff09; 审核成功后得…

android studio 实现短信验证 登录

登录 http://www.mob.com/ 注册 创建项目 加入依赖 贴代码 classpath “com.mob.sdk:MobSDK:2018.0319.1724” apply plugin: ‘com.mob.sdk’ // 在MobSDK的扩展中注册SMSSDK的相关信息 这里使用自己的 appKey appSecret MobSDK {appKey “2e2974aec0” appSecret “1d35b87…

Java简单实现短信验证登录(Session、Redis)

前端设计 <div class"login-form"><div style"display: flex; justify-content: space-between"><el-input style"width: 60%" placeholder"请输入手机号" v-model"form.phone" ></el-input><e…

Vue与Node.js实现手机短信验证登录

手机短信使用的第三方平台是联容云&#xff0c;注册就送8块钱体验费&#xff0c;足够自己用用了&#xff0c;注册完自己建一个应用就能拿到需要使用的配置了&#xff0c;如图 注册完之后1就可以使用了。 Node.js后端使用了Express框架 "js-base64": "^3.7.2&qu…

【青龙面板+诺兰2.0 网页短信验证登录+bot查询】

看这个之前&#xff0c;如果是没搭建过的先看下面这篇哈&#xff0c;如果是跟着下面的搭建完了&#xff0c;出现了机器人5次获取验证码失败&#xff0c;让你用Cookie方式登录的情况&#xff0c;看这篇哈。 前提&#xff1a;自己有服务器&#xff01;这里用的Centos7.6做演示&am…

Springboot实现短信登录验证

Springboot学习笔记——Java实现短信登录验证功能--Servlet/SSM/SpringBoot都可以用 小白记录一下短信验证登入的实现&#xff0c;方便以后可以拿来直接用。 发短信平台&#xff1a;互亿无线 官网地址 登入注册啥的就不说了&#xff0c;新人注册会送十条短信验证&#xff0c;想…

java WEB调用秒嘀科技短信验证接口(实现短信验证登录)

java WEB调用秒嘀科技短信验证接口&#xff08;实现短信验证登录&#xff09; 前言注册秒嘀云账号登录秒嘀云官网 代码 前言 短信验证登咱就不多说了&#xff0c;为什么推荐用秒嘀的呢&#xff0c;应为他会送你10元钱&#xff0c;对于新手来说10元钱&#xff0c;足够你玩了。但…

Android利用mob实现短信验证登录

首先要去官网申请一个应用&#xff0c;拿到对应的APPKEY以及APPSECRET 附上直通车链接MobTech 申请应用基本是秒批&#xff0c;然后就可以得到应用的APPKEY以及APPSECRET 然后就是查看官方的文档 直接跟着步骤走&#xff0c;可以不用手动下载sdk&#xff0c;导入这些它自动会帮…

微信小程序短信验证登录

首先小程序wxml页面 <!--pages/logins/logins.wxml--> <view class"container"><view class"title">登录</view><form catchsubmit"login"><view class"inputView"><input class"inputT…

Springboot实现短信验证登录

一、介绍 使用短信验证登录也是现在实际项目中普遍使用的一种登录, 二、实际的操作流程 1.用户在前端页面输入手机号码之后,点击发送验证码 2.前端将手机号传给后端 3.后端生成一个6为的随机数通过短信发送给用户,之后将手机号设为key,验证码设为value存入redis缓存中…

html短信验证登录

最近在做项目中遇到了一个问题&#xff0c;做的是一个后台管理。都知道&#xff0c;后台是需要一定安全性的&#xff0c;所以一定要有登录这个功能的。然而登录这个功能&#xff0c;又有一个不可或缺的因素&#xff0c;那就是验证登录。不论是图形验证也好&#xff0c;还是什么…

spring security——短信验证码登录(四)

一、导读 短信登录和用户名密码登录的逻辑是不同的&#xff0c;Spring Security 框架中实现的是用户名密码的登录方式。现在我们就模仿它的原理来加入短信登录的认证&#xff08;注意不是验证&#xff09;&#xff0c;实现右边的。 之前写的图形验证码是在 UsernamePasswordAut…

雅克比矩阵求导推导

首先&#xff0c;引入雅克比矩阵公式&#xff1a; J [ u 1 u 2 ⋯ u n e 1 e 2 ⋯ e n ] \mathbf{J}\left[\begin{array}{c} \begin{array}{lll}\mathbf{u}_{1} & \mathbf{u}_{2} & \cdots & \mathbf{u}_{n}\end{array} \\ \begin{array}{lll}\mathbf{e}_{1} &…

矩阵求导公式

转自&#xff1a;http://blog.sina.com.cn/s/blog_4a033b090100pwjq.html 求导公式(撇号为转置&#xff09;&#xff1a; Y A * X --> DY/DX A Y X * A --> DY/DX A Y A * X * B --> DY/DX A * B Y A * X * B --> DY/DX B * A 乘积的导数 d(f*g)/dx(df/dx)…

矩阵求导法则的总结

最重要的写在前面&#xff1a; 进行更新&#xff1a;随着理解的加深&#xff0c;我发现之前写的有些问题&#xff0c;重新写一下吧 矩阵求导要分成两类&#xff0c;第一类是用在向量函数f(x)里&#xff0c;比如要在x0处展开&#xff0c;所以需要计算该点处的雅可比这些&#…