【雨学习】数据结构入门---线性结构的笔记及代码实现

这篇具有很好参考价值的文章主要介绍了【雨学习】数据结构入门---线性结构的笔记及代码实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、连续存储【数组】

数组元素类型相同,大小相等

二、离散存储【链表】

定义:

        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

到了这里,关于【雨学习】数据结构入门---线性结构的笔记及代码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构 | 入门】线性表与链表 (问题引入&实现&算法优化)

    🤵‍♂️ 个人主页: @计算机魔术师 👨‍💻 作者简介:CSDN内容合伙人,全栈领域优质创作者。 本文是浙大数据结构学习笔记专栏 这里我们引入一个问题,最常见的多项式,我们如何使用编程将多项式表示出来呢? 我们可以使用数组来表示,但是会随着一个问题,如下图底

    2024年01月21日
    浏览(71)
  • 数据结构-线性表的链式存储(包含代码实现)

    链式存储结构 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻 线性表的链式存储结构又称为 非顺序映像 或 链式映像。 用一组 物理位置任意的存储单元 来存放线性表的数据元素 这组存储单元既可以是连续的,也可以是不连续的,甚至是零散

    2024年02月06日
    浏览(53)
  • 数据结构入门(C语言版)线性表带头双向循环链表接口实现

    在上一篇博客我们讲述了链表的概念和结构,还实现了无头单向非循环链表接口写法,那么这一章节,我们来实现另一种常用的链表组成结构——带头双向循环链表。 如果对前面的链表基本概念还是不了解,可以看作者的上一篇博客: 线性表中链表介绍及无头单向非循环链

    2023年04月12日
    浏览(47)
  • 数据结构入门(C语言版)线性表中顺序表介绍及接口实现

    C语言的学习结束,就该入门数据结构了呦 不论在程序员的工作上,还是在学习或是考研上,数据结构都是一门非常重要且值得我们一直研究探索的学科,可以说数据结构和算法就是编程的核心。OK,接下来我们来到数据结构的入门第一步就是学习线性表,接下来由作者来详细

    2023年04月12日
    浏览(48)
  • 数据结构笔记NO.1(绪论、线性表、栈队列和矩阵的压缩存储)

    1、数据结构 三要素 :逻辑结构、存储结构(物理结构)、数据的运算。 (1)逻辑结构:是指数据元素之间的逻辑关系,即从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。 (2)存储结构(物理结构):是指数据在计算机中的表示(又称映像),是用计算机语

    2024年02月04日
    浏览(40)
  • 【算法与数据结构】1 算法0基础入门,详解什么是算法?什么是线性查找法?

    欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享,与更多的人进行学习交流 本文收录于算法与数据结构体系专栏, 本专栏 是服务于0基础者,一起完成从0到1的跨越 就是 一系列 可以解决问题的 清晰的 可执行的 计算机指令 那么生活中有哪些算法? 问路 :坐公交车到

    2023年04月09日
    浏览(54)
  • 【考研复习】24王道数据结构课后习题代码|2.3线性表的链式表示

    删除结点:1、2、4 就地逆置:5、 合并链表 分解链表:10、11、 去除重复元素:12、 并集:14、15 循环链表:17、18、19、20 头插法、尾插法重点基础必掌握。 判断是否有环:21 用函数递归调用删除结点。 注意删除结点的时候可能断链。 利用函数调用的特性反向输出。 设置保

    2024年02月13日
    浏览(47)
  • 数据结构-线性表的顺序表基本操作代码实现(超级详细清晰 C++实现)

    顺序表是用一段 物理地址连续的存储单元 依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。 顺序表: 可动态增长的数组,要求数据是连续存储的 特点: 随机访问 顺序既可以 静态分配 ,也可以 动态分配 。在静态分配时,由于数组

    2024年02月07日
    浏览(56)
  • 数据结构之堆——算法与数据结构入门笔记(六)

    本文是算法与数据结构的学习笔记第六篇,将持续更新,欢迎小伙伴们阅读学习。有不懂的或错误的地方,欢迎交流 当涉及到高效的数据存储和检索时,堆(Heap)是一种常用的数据结构。上一篇文章中介绍了树和完全二叉树,堆就是一个完全二叉树,可以分为最大堆和最小

    2024年02月11日
    浏览(47)
  • 数据结构之栈、队列——算法与数据结构入门笔记(四)

    本文是算法与数据结构的学习笔记第四篇,将持续更新,欢迎小伙伴们阅读学习 。有不懂的或错误的地方,欢迎交流 栈是一种线性数据结构,其 只允许在固定的一端进行插入和删除 元素操作。进行数据插入和删除操作的一端称为栈顶 (Top), 另一端称为栈底 (Bottom)。栈中的

    2024年02月08日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包