一、连续存储【数组】
数组元素类型相同,大小相等
二、离散存储【链表】
定义:
n个节点离散分配,彼此通过指针相连,每个节点只有一个前驱节点,且只有一个后续节点
首节点前没有前驱节点,尾节点没有后续节点
专业术语:
首节点:第一个有效节点
尾节点:最后一个有效节点
头节点:是第一个有效节点前的节点,不存放有效数据,方便对链表的操作
头指针:指向头节点的指针变量
尾指针:指向尾节点的指针变量
只需要头指针就能对一个链表处理,指针域指向下一个节点的整体(并非单独的数据域或指针域)
分类:
单链表
双链表:每一个指针都有两个指针域
循环链表:能通过任何一个节点找到其他所有节点
非循环链表
相关算法:
遍历、查找、清空、销毁、求长度、排序、删除节点、插入节点
狭义的算法是与数据的存储方式密切相关的;
广义的算法是与数据的存储方式无关的;
泛型:利用某种技术达到的效果就是:不同的存数方式但执行的操作是一样的
连续储存数据链表的代码实现(类似于手搓数组)
#include "stdio.h"
#include "malloc.h"
#include "stdlib.h"//包含了exit函数
//定义了一个数据类型(类似于手搓数组)
struct Arr
{
int * pBase;
int len;//当前有效数组长度
int cnt;//当前有效元素个数
};
void init_arr(struct Arr * pArr,int length);//初始化
bool append_arr(struct Arr * pArr,int val);//追加
bool insert_arr(struct Arr * pArr,int pos,int val);//插入
//pos的值从1开始
bool delete_arr(struct Arr * pArr,int pos,int* pVal);//删除
int get();
bool is_empty(struct Arr * pArr);
bool is_full(struct Arr * pArr);
void sort_arr(struct Arr * pArr);//排序
void show_arr(struct Arr * pArr);
void inversion_arr(struct Arr * pArr);//倒置
int main()
{
struct Arr arr;
int val;
init_arr(&arr,6);
show_arr(&arr);
append_arr(&arr,4);
append_arr(&arr,3);
append_arr(&arr,2);
append_arr(&arr,1);
sort_arr(&arr);
/*
if(delete_arr(&arr,3,&val))
{
printf("删除成功!\n");
printf("您删除的元素是:%d\n",val);
}
else printf("删除失败!\n");
*/
/*
if(insert_arr(&arr,3,99))
{
printf("插入成功!\n");
}
else printf("插入失败!\n");
if(append_arr(&arr,7))
{
printf("追加成功!\n");
}
else printf("追加失败!\n");
*/
show_arr(&arr);
return 0;
}
//结构体变量不能加减乘除,但可以相互赋值
void init_arr(struct Arr * pArr,int length)//(arr == *pArr)
{
pArr->pBase = (int *)malloc(sizeof(int) * length);
//pArr->pBase 等价于 (*pArr).pBase
if(NULL == pArr->pBase)
{
printf("动态内存分配失败!\n");
exit(-1);//终止整个程序
}
else
{
pArr->len = length;
pArr->cnt = 0;
}
return;
}
bool is_empty(struct Arr * pArr)
{
if(0 == pArr->cnt) return true;
else return false;
}
void show_arr(struct Arr * pArr)
{
//判断数组是否为空
if(is_empty(pArr))//&pArr不是地址,pArr本身就是地址
{
//提示用户数组为空
printf("数组为空!\n");
}
else
{
for(int i=0;i<pArr->cnt;i++)
{
printf("%d ",pArr->pBase[i]);
//或者是 *(pArr->pBase+i)
}
printf("\n");
}
return;
}
bool is_full(struct Arr * pArr)
{
if(pArr->cnt == pArr->len) return true;
else return false;
}
bool append_arr(struct Arr * pArr,int val)
{
if(is_full(pArr))
{
return false;
}
//数组不满时才能追加
pArr->pBase[pArr->cnt] = val;
(pArr->cnt)++;
return true;
}
bool insert_arr(struct Arr * pArr,int pos,int val)
{
int i;
if(is_full(pArr))
{
return false;
}
if(pos<1 || pos>pArr->cnt+1)
{
return false;
}
for(i=pArr->cnt-1;i>=pos-1;i--)
{
pArr->pBase[i+1] = pArr->pBase[i];
}
pArr->pBase[pos-1] = val;
(pArr->cnt)++;
return true;
}
bool delete_arr(struct Arr * pArr,int pos,int* pVal)
{
int i;
if(is_empty(pArr))
{
return false;
}
if(pos<1 || pos>pArr->cnt)
{
return false;
}
*pVal = pArr->pBase[pos-1];
for(i=pos;i<pArr->cnt;i++)
{
pArr->pBase[i-1] = pArr->pBase[i];
}
(pArr->cnt)--;
return true;
}
void inversion_arr(struct Arr * pArr)
{
int i = 0;
int j = pArr->cnt-1;
while(i < j)//快速排序思想
{
int tmp = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = tmp;
i++;
j--;
}
return;
}
void sort_arr(struct Arr * pArr)
{
int i,j;
for(i=0;i<pArr->cnt-1;i++)
{
for(j=0;j<pArr->cnt-1-i;j++)
{
if(pArr->pBase[j]>pArr->pBase[j+1])//从小到大
{
int tmp = pArr->pBase[j];
pArr->pBase[j] = pArr->pBase[j+1];
pArr->pBase[j+1] = tmp;
}
}
}
return;
}
链表创建及相关算法的代码实现
#include "stdio.h"
#include "malloc.h"
#include "stdlib.h"//包含了exit函数
//链表的创建和链表遍历算法的实现
typedef struct Node
{
int data;//数据域
struct Node * pNext;//指针域
}NODE,*PNODE;
//NODE等价于struct Node PNODE等价于struct Node*
//函数声明
PNODE create_list(void);
void traverse_list(PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE pHead);
bool insert_list(PNODE,int ,int );
bool delete_list(PNODE,int ,int *);
void sort_list(PNODE);
int main(void)
{
PNODE pHead = NULL;
int val;
pHead = create_list();
//创建一个非循环链表,并将头节点地址赋给pHead
printf("当前的链表为:");
traverse_list(pHead);
//遍历输出
//insert_list(pHead,4,33);
//traverse_list(pHead);
if(delete_list(pHead,4,&val))
{
printf("删除成功,删除的元素是%d\n",val);
}
else printf("删除失败,您删除的元素不存在\n");
traverse_list(pHead);
//if(is_empty(pHead)) printf("链表为空\n");
//else printf("链表不空\n");
//int len = length_list(pHead);
//printf("链表的长度是:%d\n",len);
//sort_list(pHead);
//printf("排序后的链表为:");
//traverse_list(pHead);
return 0;
}
PNODE create_list(void)
{
int len;//存放有效节点的个数
int i;
int val;//临时存放用户输入节点的值
//分配一个不存放有效数据的头节点
PNODE pHead = (PNODE) malloc(sizeof(NODE));
if(NULL == pHead)
{
printf("分配失败,程序终止\n");
exit(-1);
}
PNODE pTail = pHead;
//pTail指向当前的尾节点
pTail->pNext = NULL;
//避免len=0,要把头节点指针域清空
printf("链表节点的个数:len=");
scanf("%d",&len);
for(i=0;i<len;i++)
{
printf("输入第%d个节点的值:",i+1);
scanf("%d",&val);
//将pNew挂到当前尾节点的后面
PNODE pNew = (PNODE) malloc(sizeof(NODE));
if(NULL == pNew)
{
printf("分配失败,程序终止\n");
exit(-1);
}
pNew->data = val;
pTail->pNext = pNew;
pNew->pNext = NULL;
pTail = pNew;
}
return pHead;
}
void traverse_list(PNODE pHead)
{
PNODE p = pHead->pNext;
while(NULL != p)
{
printf("%d ",p->data);
p = p->pNext;
}
printf("\n输出完毕\n");
return;
}
bool is_empty(PNODE pHead)
{
if(pHead->pNext == NULL)
{
return true;
}
else return false;
}
int length_list(PNODE pHead)
{
PNODE p = pHead->pNext;
int num = 0;
while(NULL != p)
{
num++;
p = p->pNext;
}
return num;
}
void sort_list(PNODE pHead)
{
int i,j,t;
PNODE p,q;
int len = length_list(pHead);
for(i=0,p=pHead->pNext;i<len-1;i++,p=p->pNext)
{
for(j=i+1,q=p->pNext;j<len;j++,q=q->pNext)
{
if(p->data > q->data)//类似于数组中的:a[i]>a[j]
{
t = p->data;
p->data = q->data;
q->data = t;
}
}
}
return;
}
//在pHead所指向链表的第pos个节点的前面插入一个新的节点,该节点的值是val,并且pos的值从1开始
bool insert_list(PNODE pHead,int pos,int val)
{
int i = 0;
PNODE p = pHead;
while(NULL!=p && i<pos-1)
{
p = p->pNext;
++i;
}
if(i>pos-1 || NULL==p) return false;
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if(NULL == pNew)
{
printf("动态内存分配失败\n");
exit(-1);
}
pNew->data = val;
PNODE q = p->pNext;
p->pNext = pNew;
pNew->pNext = q;
return true;
}
bool delete_list(PNODE pHead,int pos,int *pVal)
{
int i = 0;
PNODE p = pHead;
while(NULL!=p->pNext && i<pos-1)
{
p = p->pNext;
++i;
}
if(i>pos-1 || NULL==p->pNext) return false;
PNODE q = p->pNext;
*pVal = q->data;
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}
(一)线性结构的两种常见应用之一:栈
定义:
一种可以实现“先进后出”的存储结构
栈类似于箱子
头部用top,尾部用bottom
(把链表的一些功能砍掉,再添加一些功能)
分类:
静态栈
动态栈
算法:
出栈
压栈
应用:
函数调用
中断
表达式求值(计算器)
内存分配
缓冲处理
迷宫
栈的代码实现
#include "stdio.h"
#include "malloc.h"
#include "stdlib.h"
typedef struct Node
{
int data;
struct Node * pNext;
}NODE, * PNODE;
typedef struct Stack
{
PNODE pTop;
PNODE pBottom;
}STACK, * PSTACK;
void init(PSTACK);
void push(PSTACK,int);
void traverse(PSTACK);
bool pop(PSTACK,int*);
void clear(PSTACK);
int main(void)
{
STACK S;
int val;
init(&S);//初始化
push(&S,1);//压栈
push(&S,2);//压栈
push(&S,3);//压栈
push(&S,4);//压栈
push(&S,5);//压栈
push(&S,6);//压栈
traverse(&S);//遍历输出
clear(&S);//栈保留,清空栈
//traverse(&S);//遍历输出
if(pop(&S,&val))//出栈
{
printf("出栈成功,出栈元素是%d\n",val);
}
else printf("出栈失败!\n");
traverse(&S);//遍历输出
return 0;
}
void init(PSTACK pS)
{
pS->pTop = (PNODE)malloc(sizeof(NODE));
if(NULL == pS->pTop)
{
printf("动态内存分配失败\n");
exit(-1);
}
else
{
pS->pBottom = pS->pTop;
pS->pTop->pNext = NULL;
//或写pS->pBottom->pNext = NULL;
//把创建空间的指针域清空
}
}
void push(PSTACK pS,int val)
{
PNODE pNew = (PNODE)malloc(sizeof(NODE));
pNew->data = val;
pNew->pNext = pS->pTop;
pS->pTop = pNew;
return;
}
void traverse(PSTACK pS)
{
PNODE p = pS->pTop;
while(p != pS->pBottom)
{
printf("%d ",p->data);
p = p->pNext;
}
printf("\n");
return ;
}
bool is_empty(PSTACK pS)
{
if(pS->pTop == pS->pBottom)
{
return true;
}
else return false;
}
//把pS所指向的栈出栈一次,把出栈的元素存入val中
bool pop(PSTACK pS,int *pVal)
{
if(is_empty(pS))//pS本身存放的就是S的地址
{
return false;
}
else
{
PNODE r = pS->pTop;
*pVal = r->data;
pS->pTop = r->pNext;
free(r);
r = NULL;
return true;
}
}
void clear(PSTACK pS)
{
if(is_empty(pS))
{
return ;
}
else
{
PNODE p = pS->pTop;
PNODE q = NULL;
while(p != pS->pBottom)
{
q = p->pNext;
free(p);
p = q;
}
pS->pTop = pS->pBottom;
return ;
}
}
(二)线性结构的两种常见应用之二:队列
定义:
一种可以实现”先进先出“的存储结构
头部用front,尾部用rear
分类:
链式队列 --- 用链表实现
静态队列 --- 用数组实现(把数组的一些功能砍掉,再添加一些功能)
静态队列通常都必须是循环队列
附:循环队列的讲解:
1.静态队列为什么必须是循环队列
front指向第一个元素,rear指向最后一个元素的下一个元素*
f和r都只能加,f和r要循环
2.循环队列需要几个参数来确定 需要2个参数来确定
Front
Rear
3.循环队列各个参数的含义
2个参数在不同场合有不同含义
建议初学者先记住,然后慢慢体会
1)队列初始化
front和rear的值都是零
2)队列非空
front代表的是队列的第一个元素
rear代表的是队列的最后一个有效元素的下一个元素
3)队列空
front和rear的值相等,但不一定是零
4.循环队列入队伪算法讲解
1.将值存入r所代表的位置
2.r=r+1 //错误写法
正确写法为:r=(r+1)%数组长度
5.循环队列出队伪算法讲解
f=(f+1)%数组长度
6.如何判断循环队列是否已空伪算法
f和r相等时队列为空
7.如何判断循环队列是否为满伪算法
预备知识:
front的值可能比rear大
front的值也完全有可能比rear小
当然也可能两者相等
两种方法:
1.多增加一个标志参数(记录存储元素个数)
2.少用一个元素
如果r和f的值紧挨着,则队列已满
用C语言伪算法表示就是:
/*
if( (r+1)%数组长度==f ) 已满
else 不满
*/
队列算法:
入队
出队
队列的具体应用:
所有和时间有关的操作都有队列的影子(优先级)
循环队列的代码实现
#include "stdio.h"
#include "bits/stdc++.h"
#include "malloc.h"
#include "stdlib.h"
using namespace std;
typedef struct Queue
{
int * pBase;//Pbase是一个数组的首地址
int front;
int rear;
}QUEUE;
void init(QUEUE *);
bool en_queue(QUEUE *,int val);
void traverse_queue(QUEUE *);
bool full_queue(QUEUE *);
bool out_queue(QUEUE *,int * );
bool emput_queue(QUEUE *);
int main()
{
QUEUE Q;
int val;
init(&Q);
en_queue(&Q,1);
en_queue(&Q,2);
en_queue(&Q,3);
en_queue(&Q,4);
en_queue(&Q,5);
en_queue(&Q,6);
en_queue(&Q,7);
en_queue(&Q,8);
//只能存储1-5的数
traverse_queue(&Q);
if(out_queue(&Q,&val))
{
printf("出队成功,您出队的元素是%d\n",val);
}
else printf("出队失败\n");
traverse_queue(&Q);
return 0;
}
void init(QUEUE *pQ)
{
pQ->pBase = (int *)malloc(sizeof(Queue) * 6);
pQ->front = 0;
pQ->rear = 0;
}
bool full_queue(QUEUE *pQ)
{
if( (pQ->rear+1)%6 == pQ->front )
{
return true;
}
else return false;
//少用一个元素判断是否为满(第二种方法)
}
bool en_queue(QUEUE *pQ,int val)
{
if(full_queue(pQ))
{
return false;
}
else
{
pQ->pBase[pQ->rear] = val;
pQ->rear = (pQ->rear+1)%6;
return true;
}
}
void traverse_queue(QUEUE *pQ)
{
int i = pQ->front;
while(i != pQ->rear)
{
printf("%d ",pQ->pBase[i]);
i = (i+1) % 6;
}
printf("\n");
return;
}
bool emput_queue(QUEUE *pQ)
{
if(pQ->front == pQ->rear)
{
return true;
}
else return false;
}
bool out_queue(QUEUE *pQ,int *pVal)
{
if(emput_queue(pQ))
{
return false;
}
else
{
*pVal = pQ->pBase[pQ->front];
pQ->front = (pQ->front+1)%6;
return true;
}
}
专题:递归
定义:
一个函数自己直接或间接调用自己
阶乘满足三个条件:
1.递归必须得有一个明确的终止条件
2.该函数所处理的数据规模必须在递减
3.这个转化必须是可解的
循环和递归
递归:
易于理解
速度慢
存储空间浪费多
循环:
不易理解
速度快
存储空间浪费少
举例: 1.1+2+3+...+100的和
2.求阶乘
3.汉诺塔
4.走迷宫
递归的应用:
树和森林就是以递归的方式定义的
数和图的很多算法都是以递归来实现的文章来源:https://www.toymoban.com/news/detail-817407.html
很多数学公式就是以递归的方式定义的文章来源地址https://www.toymoban.com/news/detail-817407.html
到了这里,关于【雨学习】数据结构入门---线性结构的笔记及代码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!