C/C++数据结构-完整代码数据结构的理论,线性表(动态数组,链表)(完整的算法代码-增删改查-代码解析+运行结果解析)

Posted 蓝盒子itbluebox

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++数据结构-完整代码数据结构的理论,线性表(动态数组,链表)(完整的算法代码-增删改查-代码解析+运行结果解析)相关的知识,希望对你有一定的参考价值。

C/C++数据结构-完整代码(一)数据结构的理论,线性表(动态数组,链表)(完整的算法代码-增删改查-代码解析+运行结果解析)
C/C++数据结构-完整代码(二)栈(栈的顺序存储,栈的链式存储,就近匹配,中缀表达式和后缀表达式)
C/C++数据结构-完整代码(三)队列【Queue】(顺序存储,链式存储)增删改查
C/C++数据结构-完整代码(四)队列【Queue】(树和二叉树)(二叉树代码实现)

一、数据结构理论

1、数据

数据∶是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输
入给计算机处理的符号集合。数据不仅仅包括整型、实型等数值类型,还包括字符及声音、
图像、视频等非数值类型。

2、数据结构概念

数据结构是计算机存储、组织数据的方式。

数据结构是指相互之间存在一种或多种特定
关系的数据元素的集合。

通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。
数据结构往往同高效的检索算法和索引技术有关。

数据结构是计算机存储、组织数据的方式。是相互之间存在一种或多种特定关系的数据元素
集合

3、算法的概念

算法是特定问题求解步骤的描述,在计算机中表现为指令的有限序列,

算法是独立存在的一种解决问题的方法和思想。

对于算法而言,语言并不重要,重要的是思想。

(1)算法和数据结构区别

数据结构只是静态的描述了数据元素之间的关系,高效的程序需要在数据结构的基础上
设计和选择算法。

  • 算法是为了解决实际问题而设计的。
  • 数据结构是算法需要处理的问题载体。
  • 数据结构与算法相辅相成

(2)算法的比较

现在我们需要写一个求1+2+3+ …+ 100的结果程序,你应该怎么写呢?大多数人马上回写出下面c语言代码(或者其他语言)v

#include<stdio.h>
int main(){
	
	int i,sum = 0,n = 100;
	for(i = 1; i <= n;i ++){
		sum = sum + i;
	}
	printf("%d",sum);
	
	return 0;
} 

当然,如果这个问题让高斯来去做,他可能会写如下代码:

#include<stdio.h>
int main(){

    int i,sum = 0,n = 100;
    sum = (1+n)*n/2;
    printf("%d",sum);

    return 0;
}

运行结果

很显然,不论是从人类还是计算机的角度来看,下列的算法效率会高出很多,这就是一个好的算法会让你的程序更加的高效。

(3)算法的特性

输入输出:算法具有零个或多个输入、至少有一个或多个输出。

有穷性:指算法在执行有限的步骤之后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成。

确定性:算法的每一步骤都有确定的含义,不会出现二义性。

可行性:算法的每一步都必须是可行的,也就是说,每一步都能通过执行有限次数完成。

4、数据结构的分类

(1)逻辑结构

1)集合结构:

集合结构中的数据元素除了同属于一个集合外,
他们之间没有其他关系。各个数据元素是平等的。
他们共同属于同一个集合,数据结构中的集合关系类似于数学中的集合,如下图所示

2)线性结构

3)树形结构:树形结构中是数据元素之间存在一种一对多的层次关系,如图:

4)图形结构:图形结构的数据元素是多对多的关系,如果:

(2)物理结构

说完了逻辑结构,再说下物理结构,也有的书称为存储结构。
物理结构:是指数据的逻辑结构在计算机中的存储形式,共分为两种:顺序存储和链式存储o

1)顺序存储

是把数据元素存放在地址连续的存储单元里,其数据的逻辑关系和物理关系是一致的,
如图:

如果所有数据结构都很简单有规律,一切就好办了,可实际上,总有人想要插队,或者放弃排队,所以元素集合中就会添加、删除掉成员,显然面对这样时常要变化的结构,顺序存储是不科学的,那怎么办呢

2)链式存储结构

是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。

数据元素的存储关系并不能反映其逻辑关系,
因此需要用一个指针存放数据元素的地址,
这样通过地址就可以找到相关数据的位置。
如图:

5、总结

(1)算法:

1)5个特性:输入 输出 有穷 确定 可行性

(2)数据结构的分类:

1)逻辑结构

集合:元素之间没有关系,都是平等,不去探讨
线性:除了第一个元素没有前驱,最后一个元素没有后继,其他元素都有唯一的前驱和唯一的后继
树形
图形

2)物理结构

顺序存储
链式存储

二、线性表

1、线性表的基本概念

线性结构是一种最简单且常用的数据结构。线性结构的基本特点是节点之间满足线性关
系。本章讨论的动态数组、链表、栈、队列都属于线性结构。

他们的共同之处,是节点中有
且只有一个开始节点和终端节点。

按这种关系可以把它们的所有节点排列成一个线性序列。
但是,他们分别属于几种不同的抽象数据类型实现,它们之间的区别,主要就是操作的不同。

线性表是零个或者多个数据元素的有限序列,数据元素之问是有顺序的,数据元素个数
是有限的,数据元素的类型必须相同

例:先来看一个大家感兴趣的话题,一年里的星座列表,是不是线性表呢?如图所示:

线性表的性质:
1 ) a0为线性表的第一个元素,只有一个后继。
2 ) an为线性表的最后一个元素,只有一个前驱。
3 )除 a0和 an 外的其它元素ai,既有前驱,又有后继。
4)线性表能够逐项访问和顺序存取。

线性表的抽象数据类型的定义:

2、动态数组

(1)初始化

#include "stdio.h"
#include "string.h"
#include "stdlib.h"
//动态数组的结构
struct dynamicAray{
    void ** pAddr;//维护在堆区真实的数组指针
    int m_capacity;//数组的容量
    int m_size;//数组的大小
};
//初始化数组
struct dynamicAray * init_DynamicArray(int  capacity){
    //开辟到堆区
    if(capacity <= 0){
        return NULL;
    }
    struct dynamicAray * array = malloc(sizeof(struct dynamicAray ));
    //判断内存是否申请成功
	if(array == NULL){
		return; 
	} 
	//设置容量 
    array -> m_capacity = capacity;
    //设置大小 
	array -> m_size = 0;
	//维护在堆区数组的指针 
	array ->pAddr = malloc(sizeof(void*) * array-> m_capacity);
    
    return array;
}

(2)插入功能

//插入功能
void insert_dynamicArray(struct dynamicAray * array,int pos,void *data){
	if(array == NULL){
		return NULL;
	}
	if(data == NULL){
		return NULL;
	}
	if(pos < 0 || pos > array->m_size){
		//无效的位置 进行尾插
		pos = array-> m_size;//插入到当前数组的最后一个位置 
	}
	//先判断是否已经满载,如果满载的就动态的开辟
	if(array -> m_size >=array -> m_capacity){
		//1、申请一个更大的内存空间
		int newCapacity = array->m_capacity * 2;
		//2、创建新的空间
		void ** newSpace =  malloc(sizeof(void *) * newCapacity);
		//3、将原有的数据 拷贝到新空间下
		memcpy(newSpace,array->pAddr,sizeof(void  * ) * array->m_capacity); 
		//4、释放原有的空间
		free(array->pAddr);
		//5、更改指针的指向
		array -> pAddr = newSpace;
		//6、更新新容量的大小
		array -> m_capacity = newCapacity; 
	}
	//插入新的数据的元素 
	//从最后一个位置开始依次往后移动数据  后移
	int i;
	for(i = array -> m_size -1 ;i >= pos;i--){
		array->pAddr[i+1] = array->pAddr[i];
	} 
	//将新元素插入到指定的位置
	array -> pAddr[pos] = data;
	
	//更新一下大小
	array ->m_size++; 
} 

(3)遍历功能的实现

//遍历数组
void foreach_DynamicArray(struct dynamicAray * array,void(*myForeach)(void *)){
	
	if(array == NULL){
		return;
	}
	if(myForeach == NULL){
		return;
	}
	int i;
	for(i = 0; i < array ->m_size; i++)
	{
		myForeach(array->pAddr[i]);
	}
	
} 

(4)使用上述的代码实现一个完整的创建,插入和遍历

全部代码

#include "stdio.h"
#include "string.h"
#include "stdlib.h"
//动态数组的结构
struct dynamicAray{
    void ** pAddr;//维护在堆区真实的数组指针
    int m_capacity;//数组的容量
    int m_size;//数组的大小
};
//初始化数组
struct dynamicAray * init_DynamicArray(int  capacity){
    //开辟到堆区
    if(capacity <= 0){
        return NULL;
    }
    struct dynamicAray * array = malloc(sizeof(struct dynamicAray ));
    //判断内存是否申请成功
	if(array == NULL){
		return; 
	} 
	//设置容量 
    array -> m_capacity = capacity;
    //设置大小 
	array -> m_size = 0;
	//维护在堆区数组的指针 
	array ->pAddr = malloc(sizeof(void*) * array-> m_capacity);
    return array;
}
//插入功能
void insert_dynamicArray(struct dynamicAray * array,int pos,void *data){
	if(array == NULL){
		return NULL;
	}
	if(data == NULL){
		return NULL;
	}
	if(pos < 0 || pos > array->m_size){
		//无效的位置 进行尾插
		pos = array-> m_size;//插入到当前数组的最后一个位置 
	}
	//先判断是否已经满载,如果满载的就动态的开辟
	if(array -> m_size >=array -> m_capacity){
		//1、申请一个更大的内存空间
		int newCapacity = array->m_capacity * 2;
		//2、创建新的空间
		void ** newSpace =  malloc(sizeof(void *) * newCapacity);
		//3、将原有的数据 拷贝到新空间下
		memcpy(newSpace,array->pAddr,sizeof(void  * ) * array->m_capacity); 
		//4、释放原有的空间
		free(array->pAddr);
		//5、更改指针的指向
		array -> pAddr = newSpace;
		//6、更新新容量的大小
		array -> m_capacity = newCapacity; 
	}
	//插入新的数据的元素 
	//从最后一个位置开始依次往后移动数据  后移
	int i;
	for(i = array -> m_size -1 ;i >= pos;i--){
		array->pAddr[i+1] = array->pAddr[i];
	} 
	//将新元素插入到指定的位置
	array -> pAddr[pos] = data;
	
	//更新一下大小
	array ->m_size++; 
} 
//遍历数组
void foreach_DynamicArray(struct dynamicAray * array,void(*myForeach)(void *)){
	
	if(array == NULL){
		return;
	}
	if(myForeach == NULL){
		return;
	}
	int i;
	for(i = 0; i < array ->m_size; i++)
	{
		myForeach(array->pAddr[i]);
	}
	
} 

struct Person{
	char name[64];
	int age;
};

void myPrintPerson(void * data){
	
	struct Person * p = data;
	printf("姓名:%s,年龄%d\\n",p->name,p->age);
	
}

void test01(){
	//创建 动态数组
	struct dynamicAray * arr = init_DynamicArray(5); 
	
	//准备出5个person 数据
	struct Person p1 = {"亚瑟",28 }; 
	struct Person p2 = {"王昭君",18 }; 
	struct Person p3 = {"赵云",38 }; 
	struct Person p4 = {"张飞",38 }; 
	struct Person p5 = {"关羽",28 }; 
	struct Person p6 = {"宫本",88 }; 
	
	//将数据插入到动态数组当中
	insert_dynamicArray(arr,0,&p1);
	insert_dynamicArray(arr,0,&p2);
	insert_dynamicArray(arr,0,&p3);
	insert_dynamicArray(arr,2,&p4);
	insert_dynamicArray(arr,10,&p5);
	insert_dynamicArray(arr,1,&p6);
	
	
	//赵云 宫本 王昭君 张飞  亚瑟 关羽
	//遍历动态数组
	foreach_DynamicArray(arr,myPrintPerson); 
	
	
}
 

int main(){

	test01();
	return 0;
}

运行结果

完善一下上述代码

全部代码

#include "stdio.h"
#include "string.h"
#include "stdlib.h"
//动态数组的结构
struct dynamicAray{
    void ** pAddr;//维护在堆区真实的数组指针
    int m_capacity;//数组的容量
    int m_size;//数组的大小
};
//初始化数组
struct dynamicAray * init_DynamicArray(int  capacity){
    //开辟到堆区
    if(capacity <= 0){
        return NULL;
    }
    struct dynamicAray * array = malloc(sizeof(struct dynamicAray ));
    //判断内存是否申请成功
	if(array == NULL){
		return; 
	} 
	//设置容量 
    array -> m_capacity = capacity;
    //设置大小 
	array -> m_size = 0;
	//维护在堆区数组的指针 
	array ->pAddr = malloc(sizeof(void*) * array-> m_capacity);
    return array;
}
//插入功能
void insert_dynamicArray(struct dynamicAray * array,int pos,void *data){
	if(array == NULL){
		return NULL;
	}
	if(data == NULL){
		return NULL;
	}
	if(pos < 0 || pos > array->m_size){
		//无效的位置 进行尾插
		pos = array-> m_size;//插入到当前数组的最后一个位置 
	}
	//先判断是否已经满载,如果满载的就动态的开辟
	if(array -> m_size >=array -> m_capacity){
		//1、申请一个更大的内存空间
		int newCapacity = array->m_capacity * 2;
		//2、创建新的空间
		void ** newSpace =  malloc(sizeof(void *) * newCapacity);
		//3、将原有的数据 拷贝到新空间下
		memcpy(newSpace,array->pAddr,sizeof(void  * ) * array->m_capacity); 
		//4、释放原有的空间
		free(array->pAddr);
		//5、更改指针的指向
		array -> pAddr = newSpace;
		//6、更新新容量的大小
		array -> m_capacity = newCapacity; 
	}
	//插入新的数据的元素 
	//从最后一个位置开始依次往后移动数据  后移
	int i;
	for(i = array -> m_size -1 ;i >= pos;i--){
		array->pAddr[i+1] = array->pAddr[i];
	} 
	//将新元素插入到指定的位置
	array -> pAddr[pos] = data;
	
	//更新一下大小
	array ->m_size++; 
} 
//遍历数组
void foreach_DynamicArray(struct dynamicAray * array,void(*myForeach)(void *)){
	
	if(array == NULL){
		return;
	}
	if(myForeach == NULL){
		return;
	}
	int i;
	for(i = 0; i < array ->m_size; i++)
	{
		myForeach(array->pAddr[i]);
	}
	
} 

struct Person{
	char name[64];
	int age;
};

void myPrintPerson(void * data){
	
	struct Person * p = data;
	printf("姓名:%s,年龄%d\\n",p->name,p->age);
	
}

void test01(以上是关于C/C++数据结构-完整代码数据结构的理论,线性表(动态数组,链表)(完整的算法代码-增删改查-代码解析+运行结果解析)的主要内容,如果未能解决你的问题,请参考以下文章

C/C++数据结构-完整代码队列Queue(顺序存储,链式存储)增删改查

C/C++数据结构-完整代码队列Queue(树和二叉树)(二叉树代码实现)

C/C++数据结构-完整代码受限线性表:栈(栈的顺序存储,栈的链式存储,就近匹配,中缀表达式和后缀表达式)

C/C++数据结构-完整代码栈(栈的顺序存储,栈的链式存储,就近匹配,中缀表达式和后缀表达式)

C/C++数据结构-完整代码队列Queue(顺序存储,链式存储)增删改查

22计算机408考研—数据结构—线性表栈队列数组