数据结构学习笔记(1.大O表示法和顺序表)

  最近在看国嵌唐老师的数据结构视频,觉得还不错,所以就把笔记记录下来

本节知识点:

1.数据之间的逻辑结构:
   集合结构:数据元素之间没有特别的关系,仅同属相同集合
   线性结构:数据元素之间是一对一的关系
   树形结构:数据元素之间存在一对多的层次关系
   图形结构:数据元素之间是多对多的关系
2.数据之间的物理结构
    顺序存储结构:将数据存储在地址连续的存储单元里
    链式存储结构:将数据存储在任意的存储单元里,通过保存地址的方式找到相关的数据元素
3.数据结构是相互之间存在一种或多种特定关系的数据元素的集合
4.程序 = 数据结构 + 算法
5.大O表示法:算法效率严重依赖于操作数量, 首先关注操作数的最高次项,操作数的估计可以作为时间和空间复杂度的估算,在没有特殊说明的时候, 我们应该分析复杂度的最坏情况
6.常见的复杂度类型:
大小关系:
7.线性表是零个或多个数据元素的集合,之间的元素是有顺序的,个数是有限的,数据类型必须相同。线性表包含两种存储方式,一种是顺序表,另一种链表。
8.对于线性表的使用是这样的:应该是在设计算法的时候,考虑算法中使用的数据,这些数据之间是什么关系的,如果是符合线性表特质的,就选择线性表作为数据结构。
9.顺序表与数组的关系:其实顺序表就是在数组的基础上构建的,本质跟数组是一样的,只是在数组的基础上增加了length长度,capacity容量等特性,然后补充了一些列,增、删、改、查的功能。
10. 我觉得链表比顺序表最大的优势,就在于链表的删除和插入要比顺序表简单的多,而且当线性表长度很大的时候很难开辟出整段的连续空间!!!最重要的是顺序表在创建的时候长度就固定了,再也改变不了了,而链表则可以根据情况动态增加,这一点是顺序表无论怎么样都不可能实现的!!!
 
顺序表的优点是:无需为线性表中的逻辑增加额外的空间,可以快速的通过下标的方式找到表中的合法位置。
11.线性表的常用操作:创建线性表、销毁线性表、清空线性表、将元素插入线性表、将元素从线性表中删除、获取线性表中某个位置的元素、获取线性表的长度

本节代码:

1.本节的代码是一个可以适合各种类型的顺序表,之所以能够适合各种类型,是因为它在顺序表中保存的是元素的地址(其实就是一个指针数组)。
2.代码中的描述顺序表的结构体中的元素介绍:length是顺序表中有元素的个数、capacity是顺序表的容量、node是顺序表的头地址(也是这个指针数组的头地址)、还有一个就是pos,pos是在删除和插入的时候使用的一个参数,它代表的是插入到顺序表位置的下标(数组的下标 是从0开始的 这个很要注意)。顺序表中有length个元素 下标是从0到length-1的。 要注意的是 操作顺序表不同功能函数的pos的允许范围是不一样的。
3.本节代码对于函数参数的合法性判断是极其重视的,这个规范是值得学习的。
4.本节代码中对于顺序表的操作函数,凡是外界输入的,和输出到外界的,都是void *类型的,这样就保证了只有在这些操作函数中才能去改变   描述顺序表的结构体里面的值,在其他文件的函数中接受到的都是void *类型,无法直接给这个结构体中的值进行改变,这样的封装,保证了代码的安全性。
5.对于本节代码最值得思考的地方,常见的顺序表是typedef一个A类型,然后在顺序表中定义一个这个A类型的数组和length顺序表元素个数,这个顺序表中是好多个A类型的顺序集合,占用空间的大小是sizeof(A)*capacity。而本节的顺序表中是好多个unsigned int *地址类型的顺序集合,表中只有地址,第一节省了顺序表的空间,第二这样可以变相的保存不同类型的数据,第三它实现了 顺序表(即数据结构) 和 我们打算利用的数据(即元素)的分离。例如:linux内核链表(一个双向循环链表)就是一套单独的链表体制,这个链表用在很多机制上面,它就是变相的存储了好多类型的数据,并且实现了链表和数据的分离。
所以在main.c中  数据要想保存在这个顺序表中  就应该先给这些数据开辟内存    因为顺序表中没有他们呆的地方   顺序表中只能保存他们的地址。
如图:
代码如下:
Seqlist.c:
/************************************************************************************ 
文件名:Seqlist.c
头文件:Seqlist.h 
时间: 2013/08/05 
作者: Hao 
功能:可以复用 带有增 删 改 查 功能的顺序表
难点:1.顺序表中存放的都是 各种数据的地址
      2.void *是用来隔离封装用的 保证顺序表结构体只能被特定的函数改变 
************************************************************************************/
#include <stdio.h>
#include <malloc.h>
#include "Seqlist.h"

typedef unsigned int TSeqListNode;//这个顺序表中存放的是 各种数据的地址 所以用unsigned int 
typedef struct str_SeqList
{
	int length;//顺序已用的长度 
	int capacity;//顺序表的总容量 
	TSeqListNode* node;//这个指针是用来在顺序表中游走读取数据用的 
}TSeqList;  //定义描述顺序表的结构体 

/************************************************************************************ 
函数名:   Creat_SeqList
函数功能: 创建一个容量为capacity的顺序表 
参数:     int capacity 创建顺序表中成员的个数 即顺序表容量
返回值:   void* ret 如果返回NULL 说明创建顺序表失败
                     如果返回ret 说明创建顺序表成功  且ret为描述顺序表的结构体 
************************************************************************************/
SeqList* Creat_SeqList(int capacity)
{
	TSeqList* ret = NULL;
	/*进入函数 第一点是先判断传人参数的合法性*/
	if(capacity >= 0)
	{
		/*给顺序表开辟空间*/
		ret=(TSeqList* )malloc(sizeof(TSeqList)+sizeof(TSeqListNode)*capacity);
		if(NULL!=ret)//空间开辟成功   给描述顺序表的结构体 赋值 
		{
			ret->capacity=capacity;
			ret->length=0;
			ret->node=(TSeqListNode* )(ret+1);//把真正顺序表的地址赋给 node 
		}
	}
	else
	{
		ret = NULL;
	} 
	return (SeqList*)(ret);
} 

/************************************************************************************ 
函数名:   Destroy_SeqList
函数功能: 销毁顺序表   free开辟的内存 
参数:     void* list 描述顺序表结构体指针
返回值:   void 
************************************************************************************/
void  Destroy_SeqList(SeqList* list)
{
	free(list);
}

/************************************************************************************ 
函数名:  Get_Seqlist_Length
函数功能:获得顺序表 现在的大小
函数参数:void* list 描述顺序表结构体指针
函数返回值:int ret  成功返回length
                     失败返回-1 
************************************************************************************/
int Get_Seqlist_Length(SeqList* list)
{
	int ret;
	TSeqList *Tlist=(TSeqList* )list;
	/*函数参数合法性检测*/
	if(NULL != Tlist)
	{
		ret=Tlist->length;
	} 
	else
		ret=-1;
	return ret;
}

/************************************************************************************
函数名:  Get_Seqlist_Capacity
函数功能:获得顺序表 的容量 
函数参数:void* list 描述顺序表结构体指针
函数返回值:int ret  成功返回capacity 
                     失败返回-1 
************************************************************************************/
int Get_Seqlist_Capacity(SeqList* list)
{
	int ret;
	TSeqList *Tlist=(TSeqList* )list;
	/*函数参数合法性检测*/
	if(NULL != Tlist)
	{
		ret = Tlist->capacity;
	} 
	else
		ret=-1;
	return ret;
}

/************************************************************************************ 
函数名:  Clean_Seqlist_Length
函数功能:清空顺序表  其实就是给length=0; 
函数参数:void* list 描述顺序表结构体指针
函数返回值:int ret  成功返回0
                     失败返回-1 
************************************************************************************/
int Clean_Seqlist_Length(SeqList* list)
{
	int ret;
	TSeqList *Tlist=(TSeqList* )list;
	/*函数参数合法性检测*/
	if(NULL != Tlist)
	{
		Tlist->length=0;
		ret=0;
	} 
	else
		ret=-1;
	return ret;
}

/************************************************************************************
函数名:  Seqlist_Add
函数功能:顺序表中有length个数据  在下标为pos的位置上 插入数据node  所以pos是从0开始的 length是从1开始的 
参数:      SeqList* list描述顺序表的结构体地址   SeqListNode* node插入顺序表的数据的地址  
		   int pos插入顺序表的位置   pos的范围是从0(此时在顺序表头部插入)开始  到length(此时就是在顺序尾部插入)
		    总共是length+1个位置 
返回值 :  返回1 说明插入数据成功  返回0 说明插入数据失败
************************************************************************************/
int Seqlist_Add(SeqList* list, SeqListNode* node ,int pos)
{
	/*参数合法性检测*/
	TSeqList *Tlist=(TSeqList* )list;
	int ret = (NULL != list);
	int i;
	ret=ret && (pos >= 0);
	ret=ret && (Tlist->length+1 <= Tlist->capacity);  //判断再插入一个数据的时候  length有没有超过 capacity 
	if(1 == ret)
	{
		if(pos >= Tlist->length)//如果插入的位置pos比 length大的话 默认把length+1赋值给pos 
		{
			pos = Tlist->length;
		}
		for(i=Tlist->length;i>pos;i--)
		{
			Tlist->node[i]=Tlist->node[i-1];
		} 
		Tlist->node[i]=(TSeqListNode)node; //把要插入的地址强制类型转换成 unsigned int* 
		Tlist->length++;
	} 
	return ret;//返回1 说明插入数据成功  返回0 说明插入数据失败 
}	

 
/************************************************************************************
函数名:   Get_Node
函数功能:找到顺序表中下标为pos的值  
参数:    pos插入顺序表的下标   pos的范围是从0到length-1   
          SeqList* list描述顺序表的结构体地址
返回值:  void* ret 找到pos为下标的那个值
        如果成功返回pos为下标的那个值   如果失败  返回NULL
************************************************************************************/

SeqListNode* Get_Node(SeqList* list, int pos)
{
	TSeqList* Tlist=(TSeqList* )list;
	SeqListNode* ret=NULL;
	if( (NULL!=Tlist) && (pos>=0) && (pos<Tlist->length) )
	{
		ret=(SeqListNode* )Tlist->node[pos]; //强制类型转换成void*  
	}
	return ret;
} 

/************************************************************************************
函数名:   Del_Node
函数功能:找到顺序表中下标为pos的值  并且删除它 
参数:    删除pos为下标的值   pos的范围是从0到length-1   
          SeqList* list描述顺序表的结构体地址
返回值:  void* ret 
          如果成功返回pos为下标的那个值   如果失败  返回NULL 
************************************************************************************/
SeqListNode* Del_Node(SeqList* list, int pos)
{
	TSeqList* Tlist=(TSeqList* )list;
	SeqListNode* ret=NULL;
	int i;
	if( (NULL!=Tlist) && (pos>=0) && (pos<Tlist->length) )
	{
		ret=(SeqListNode* )Tlist->node[pos];
		for(i=pos+1; i<Tlist->length; i++)
		{
			Tlist->node[i-1]=Tlist->node[i];
		}
		Tlist->length--;
	}
	return ret;
}



Seqlist.h:
#ifndef __Seqlist__
#define __Seqlist__

typedef void SeqList;  //是用来封装 使顺序表结构体 不被外界改变 只可被Seqlist.c文件中的函数改变
                       //因为 这些函数 对外的接口 都是void*  
typedef void SeqListNode;//SeqList 是用来表示 顺序表的    SeqListNode是用来表示顺序表 中变量的 

SeqList* Creat_SeqList(int capacity);
void  Destroy_SeqList(SeqList* list);
int Get_Seqlist_Length(SeqList* list);
int Get_Seqlist_Capacity(SeqList* list);
int Clean_Seqlist_Length(SeqList* list);
int Seqlist_Add(SeqList* list, SeqListNode* node ,int pos);
SeqListNode* Get_Node(SeqList* list, int pos);
SeqListNode* Del_Node(SeqList* list, int pos); 

#endif

main.c:
#include <stdio.h>
#include <stdlib.h>
#include "Seqlist.h"
int main(int argc, char *argv[])
{
	SeqList* My_SeqList = NULL;
	int a = 10;
	int b = 5;
	int c = 3;
	int d = 6;
	int e = 1;
	int *p = NULL;
	int i = 0;
	My_SeqList = Creat_SeqList(5);
	if( NULL != My_SeqList )
	{
			Seqlist_Add(My_SeqList, &a ,0);
			Seqlist_Add(My_SeqList, &b ,0);
			Seqlist_Add(My_SeqList, &c ,0);
			Seqlist_Add(My_SeqList, &d ,0);
			Seqlist_Add(My_SeqList, &e ,0);
			
			for(i=0; i<Get_Seqlist_Length(My_SeqList); i++)
			{
				p=Get_Node(My_SeqList, i);
				printf("%d\n",*p);
			}
			
			Del_Node(My_SeqList, 3);
			for(i=0; i<Get_Seqlist_Length(My_SeqList); i++)
			{
				p=Get_Node(My_SeqList, i);
				printf("%d\n",*p);
			}
			
	} 
	Clean_Seqlist_Length(My_SeqList);
	Destroy_SeqList(My_SeqList);
	return 0;
}


仔细观察下面错误代码,test_main.c:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "Seqlist.h"

typedef struct student
{
	int student_num;
	char name[30];
	char sex[20];
	int age;
}str;
int main()
{
	str* str1;
	SeqList* slist=NULL;
	int i=0;
	int age=0;
	slist=Creat_SeqList(50);
	if(NULL == slist)
	{
		printf("malloc error!!!\n");
		return -1;
	}
	for(i=0; i<3; i++)
	{
		put_student(slist, str1);
	}
	
	printf("输入你要删除的年龄:\n");
	scanf("%d",&age);
	printf("\n");
	find_student(slist, str1, age);
	get_student(slist, str1);
	
	destroy_student(slist, str1);
	Clean_Seqlist_Length(slist);
	Destroy_SeqList(slist);
	return 0;
}

int put_student(SeqList* slist, str* str1)
{ 
	int num;
	int ret=(NULL != str1);
	if(1 == ret)
	{
		ret=ret && Seqlist_Add(slist, (str* )malloc(sizeof(str)*1) ,50);
		num = Get_Seqlist_Length(slist);
		str1 = (str* )Get_Node(slist, num-1);
		printf("请输入学生学号:\n"); 
		scanf("%d",&str1->student_num);
		printf("请输入学生姓名:\n");
		scanf("%s",str1->name);
		printf("请输入学生性别:\n");
		scanf("%s",str1->sex);
		printf("请输入学生年龄:\n");
		scanf("%d",&str1->age);
		printf("\n"); 
	}		
	else
	{
		ret = 0;
	}
	return ret;
}

int get_student(SeqList* slist, str* str1)
{
	int ret=(NULL != str1);
	int i=0;
	if(1 == ret)
	{
		for(i=0; i<Get_Seqlist_Length(slist); i++)
		{
			str1 = (str*)Get_Node(slist, i);
			printf("学生学号:%d\n",str1->student_num);
		
			printf("学生姓名:%s\n",str1->name);
			
			printf("学生性别:%s\n",str1->sex);
			
			printf("学生年龄:%d\n",str1->age);
		}
	}		
	else
	{
		ret = 0;
	}
	return ret;
}

int destroy_student(SeqList* slist, str* str1)
{
	int ret=(NULL != str1);
	int i=0;
	if(1 == ret)
	{
		for(i=0; i<Get_Seqlist_Length(slist); i++)
		{
			str1 = (str*)Get_Node(slist, i);
			free(str1);
		}
	}		
	else
	{
		ret = 0;
	}
	return ret;
}

int find_student(SeqList* slist, str* str1, int age)
{
	int ret=(NULL != str1);
	int i=0;
	int num=0;
	if(1 == ret)
	{
		num=Get_Seqlist_Length(slist);
		for(i=0; i<num; i++)
		{
			str1 = (str*)Get_Node(slist, i);
			if(str1->age == age)
			{
				Del_Node(slist, i);
				num=Get_Seqlist_Length(slist);
				i--;
			}
		}
	}		
	else
	{
		ret = 0;
	}
	return ret;
}





  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
逻辑结构:描述数据元素之间的逻辑关系,如线性结构(如数组、链表)、树形结构(如二叉树、堆、B树)、图结构(有向图、无向图等)以及集合和队列等抽象数据类型。 存储结构(物理结构):描述数据在计算机中如何具体存储。例如,数组的连续存储,链表的动态分配节点,树和图的邻接矩阵或邻接表表示等。 基本操作:针对每种数据结构,定义了一系列基本的操作,包括但不限于插入、删除、查找、更新、遍历等,并分析这些操作的时间复杂度和空间复杂度。 算法: 算法设计:研究如何将解决问题的步骤形式化为一系列指令,使得计算机可以执行以求解问题。 算法特性:包括输入、输出、有穷性、确定性和可行性。即一个有效的算法必须能在有限步骤内结束,并且对于给定的输入产生唯一的确定输出。 算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法,分支限界法等。 算法分析:通过数学方法分析算法的时间复杂度(运行时间随数据规模增长的速度)和空间复杂度(所需内存大小)来评估其效率。 学习算法与数据结构不仅有助于理解程序的内部工作原理,更能帮助开发人员编写出高效、稳定和易于维护的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值