C++笔记day21 数据结构1 线性表:动态数组 单向链表

167 阅读7分钟

算法的特性:输入、输出、有穷性、确定性和可行性

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

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

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

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

数据结构的分类:逻辑结构、物理结构

逻辑结构

集合结构

通常不去探讨。

线性结构

线性结构和数组元素之间是一对一的关系。除了第一个元素没有前驱,最后一个元素没有后继,其他的元素都有唯一的前驱和后继,这样一种一对一的关系。链表和数组都是线性结构。

树形结构

树形数据结构的数据元素一对多的关系,唯一的前驱对应多个后继。

图形结构

图形数据结构的数据元素是多对多的关系。

物理结构

顺序存储

连续的空间存储。

链式存储

存储单元可以是连续的也可以是不连续的。

线性表

动态数组、链表、栈、队列都属于线性结构。

线性表-动态数组

image.png

设计

 struct dynamicArray

属性: 
    void ** pAddr 维护真实在堆区创建的数组的指针
    int m_capacity; 数组容量
    int m_size; 数组大小

动态数组初始化

struct dynamicArray * init_DynamicArray(int capacity)

插入数组

image.png

void insert_DynamicArray(struct dynamicArray * array , int pos  , void * data)

遍历数组

void foreach_DynamicArray(struct dynamicArray * array, void(*myPrint)(void*))

删除数组

按照位置删除 

void removeByPos_DynamicArray(struct dynamicArray * array , int pos)

按照值删除

void removeByValue_DynamicArray(struct dynamicArray * array , void * data , int (* myCompare)(void * ,void *))

销毁数组

void destroy_DynamicArray(struct dynamicArray* array)

示例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <Windows.h>

//动态数组结构体
struct dynamicArray 
{
	void** pAddr;//维护真实在堆区创建的指针

	int mCapacity; //数组容量

	int mSize;//数组大小
};

//初始化数组
struct dynamicArray* initDynamicArray(int capacity) 
{
	if (capacity <= 0) 
	{
		return NULL;
	}
	//给数组分配空间
	struct dynamicArray *array =  malloc(sizeof(struct dynamicArray));
	if (array == NULL) 
	{
		return NULL;
	}
	array->pAddr = malloc(sizeof(void*) * capacity);
	array->mCapacity = capacity;
	array->mSize = 0;

	return array;
}

//插入数组
void insertDynamicArray(struct dynamicArray* array, int pos, void * data) 
{
	if (array == NULL) 
	{
		return;
	}
	if (data == NULL) 
	{
		return;
	}

	//无效位置 尾插
	if (pos <0 || pos >array->mSize) 
	{
		pos = array->mSize;
	}

	//判断是否满了,进行动态扩展
	if (array->mSize == array->mCapacity) 
	{
		// 1. 申请更大的内存空间
		int newCapacity = array->mCapacity * 2;
		//2. 创建新空间
		void** newSpace = malloc(sizeof(void*) * newCapacity);
		//3. 将原有数据拷贝到新空间下,用内存拷贝
		memcpy(newSpace, array->pAddr, sizeof(void *)*array->mCapacity);
		//4. 释放原有内存空间
		free(array->pAddr);
		//5. 更新新空间的指向
		array->pAddr = newSpace;
		//6. 更新心容量
		array->mCapacity = newCapacity;
	}
	//插入新元素
	//移动元素,进行插入新元素
	for (int  i = array->mSize-1; i>=pos;i--)
	{
		//数据向后移动
		array->pAddr[i + 1] = array->pAddr[i];
	}
	//将新元素,插入到制定位置上
	array->pAddr[pos] = data;
	array->mSize++;
}

//遍历数组
void foreachDynamicArray(struct dynamicArray* array,void (*myPrint)(void *)) 
{
	if (array == NULL) 
	{
		return;
	}
	if (myPrint == NULL) 
	{
		return;
	}
	for (size_t i = 0; i < array->mSize; i++)
	{
		myPrint(array->pAddr[i]);
	}
}

//删除数组 按位置删除
void removeByPosDynamicArray(struct dynamicArray* array, int pos) 
{
	if (NULL == array) 
	{
		return;
	}
	if (pos <0 || pos > array->mSize - 1) 
	{
		return;
	}
	//数据前移
	for (size_t i = pos; i < array->mSize-1; i++)
	{
		array->pAddr[i] = array->pAddr[i + 1];
	}
	//更新数组的大小
	array->mSize--;
}

//删除数组 按值删除
void removeByValueDynamicArray(struct dynamicArray* array, void *data,int (* myCompare)(void *,void*))
{
	if (array ==NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}for (size_t i = 0; i < array->mSize-1; i++)
	{
		if (myCompare(array->pAddr[i], data)) 
		{
			//如果找到要删除的数据,i就是要删除的具体位置
			removeByPosDynamicArray(array, i);
			break;
		}
	}
}

// 销毁数组
void destroyDynamicArray(struct dynamicArray* array) 
{
	if (array == NULL) 
	{
		return;
	}
	if (array->pAddr != NULL) 
	{
		free(array->pAddr);
		array->pAddr = NULL;
	}
	free(array);
	array = NULL;
}

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

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

int myComparePerson(void* data1, void* data2) 
{
	struct Person* p1 = data1;
	struct Person* p2 = data2;

	return strcmp(p1->name, p2->name) == 0 && p1->age == p2->age;
}

int main(void)
{
	//初始化动态数组
	struct dynamicArray *array = initDynamicArray(5);

	printf("插入数据前capacity = %d, size = %d\n", array->mCapacity, array->mSize);
	//准备数据
	struct Person p1 = { "Andy",18 };
	struct Person p2 = { "Judy",28 };
	struct Person p3 = { "Tony",19 };
	struct Person p4 = { "Cady",25 };
	struct Person p5 = { "Fudy",24 };
	struct Person p6 = { "Mary",21 };

	//插入数据
	insertDynamicArray(array, 0, &p1);
	insertDynamicArray(array, 0, &p2);
	insertDynamicArray(array, 1, &p3);
	insertDynamicArray(array, 0, &p4);
	insertDynamicArray(array, -1, &p5);
	insertDynamicArray(array, 0, &p6);

	//Mary Cady Judy Tony Andy Fudy  
	//遍历数据
	foreachDynamicArray(array, myPrintPerson);

	printf("插入数据后capacity = %d, size = %d\n", array->mCapacity, array->mSize);

	//测试删除
	removeByPosDynamicArray(array, 2);
	printf("------------------\n");
	foreachDynamicArray(array, myPrintPerson);

	struct Person p = { "Tony",19 };
	removeByValueDynamicArray(array, &p,myComparePerson);
	printf("------------------\n");
	foreachDynamicArray(array, myPrintPerson);

	//销毁数组
	destroyDynamicArray(array);
	array = NULL;

	system("pause");
	return EXIT_SUCCESS;
}

实现分文件编写

头文件里声明,c文件里做实现,复用的话到时候把这两个拷走就行了

线性表-单向链表(动态数组是顺序存储,链表是链式存储)

设计

image.png

struct LinkNode  节点结构体

struct LList      链表结构体

typedef  void *  LinkList  给用户使用链表指针

初始化链表

LinkList init_LinkList()

插入链表

void insert_LinkList(LinkList list, int pos, void * data)

遍历链表

void foreach_LinkList(LinkList list, void(*myForeach)(void *))

删除链表

按照位置void removeByPos_LinkList(LinkList list, int pos)

按照值void removeByValue_LinkList(LinkList list , void * data ,  int(*myCompare)(void * ,void *) )

清空

void clear_LinkList(LinkList list)

返回链表长度

int  size_LinkList(LinkList list)

销毁

void destroy_Linklist(LinkList list)

示例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


//节点的结构体设计
struct LinkNode 
{
	//数据域
	void* data;
	//指针域
	struct LinkNode* next;
};

//链表结构体
struct LList 
{
	//头节点
	struct LinkNode pHeader;
	//链表长度
	int mSize;
};

typedef void* LinkList;

//初始化链表
LinkList initLinkList()
{
	struct LList* myList = malloc(sizeof(struct LList));

	if (myList == NULL)
	{
		return NULL;
	}
	myList->pHeader.data = NULL;
	myList->pHeader.next = NULL;
	myList->mSize = 0;

	return myList;
}

//插入链表
void insertLinkList(LinkList list, int pos, void* data) 
{
	if (list == NULL) 
	{
		return;
	}
	if (data == NULL) 
	{
		return;
	}
	//将list还原成struct LList 数据类型
	struct LList* myList = list;
	if (pos <0 || pos > myList->mSize) 
	{
		//无效位置,强制做尾插
		pos = myList->mSize;
	}
	
	
	//找到插入节点的前驱节点位置
	struct LinkNode* pCurrent = &myList->pHeader;

	for (int i = 0; i < pos; i++)
	{
		pCurrent = pCurrent->next;
	}
	//pCurrent是要插入节点的前驱
	
	//创建新节点 
	struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
	newNode->data = data;
	newNode->next = NULL;

	//建立节点关系
	newNode->next = pCurrent->next;
	pCurrent->next = newNode;

	//更新链表长度
	myList->mSize++;
}

//遍历链表
void foreachLinkList(LinkList list,void(* myForeach)(void*))
{
	if (list == NULL)
	{
		return;
	}
	struct LList* myList = list;

	struct LinkNode* pCurrent = myList->pHeader.next;

	for (size_t i = 0; i < myList->mSize; i++)
	{
		myForeach(pCurrent->data);
		pCurrent = pCurrent->next;
	}
}

//删除链表 按位置
void removeLinkListByPos(LinkList list, int pos) 
{
	if (list == NULL)
	{
		return;
	}

	struct LList* myList = list;

	if (pos <0 || pos > myList->mSize-1)
	{
		//无效位置,强制做尾插
		return;
	}
	//找到待删除节点的前驱节点
	struct LinkNode* pCurrent = &myList->pHeader;
	for (size_t i = 0; i < pos; i++)
	{
		pCurrent = pCurrent->next;
	}
	//记录待删除的节点
	struct LinkNode* pDel = pCurrent->next;

	//重新建立节点关系
	pCurrent->next = pDel->next;
	free(pDel);
	pDel = NULL;

	//更新链表长度
	myList->mSize--;
}

//删除链表 按照值
void removeLinkListByValue(LinkList list, void* data,int(*myCompare)(void *,void *))
{
	if (list == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}
	struct LList* myList = list;
	//创建两个辅助指针
	struct LinkNode* pPre = &myList->pHeader;
	struct LinkNode* pCurrent = myList->pHeader.next;
	for (size_t i = 0; i < myList->mSize; i++)
	{
		//利用回调将两个指针的比较 交给用户
		if (myCompare(pCurrent->data, data)) 
		{
			pPre->next = pCurrent->next;
			
			free(pCurrent);
			pCurrent = NULL;

			myList->mSize--;
			break;
		}
		//辅助指针后移
		pPre = pCurrent;
		pCurrent = pCurrent->next;
	}
}

//清空链表
void clearLinkList(LinkList list) 
{
	if (list ==NULL)
	{
		return;
	}

	struct LList* myList = list;
	struct LinkNode* pCurrent = myList->pHeader.next;

	for (size_t i = 0; i < myList->mSize; i++)
	{
		struct LinkNode* pNext = pCurrent->next;
		free(pCurrent);
		pCurrent = pNext;
	}
	myList->pHeader.next = NULL;
	myList->mSize = 0;
}

//返回链表长度
int sizeLinkList(LinkList list) 
{
	if (list ==NULL)
	{
		return -1;
	}
	struct LList* myList = list;
	return myList->mSize;
}

//销毁链表
void destroyLinkList(LinkList list) 
{
	if (list == NULL)
	{
		return;
	}

	//清空链表
	clearLinkList(list);

	free(list);

	list = NULL;

}

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

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

int myComparePerson(void* data1, void* data2) 
{
	struct Person* p1 = data1;
	struct Person* p2 = data2;

	return strcmp(p1->name, p2->name) ==0 && p1->age == p2->age;
}

void test01() 
{
	//准备数据
	struct Person p1 = { "Andy",18 };
	struct Person p2 = { "Judy",28 };
	struct Person p3 = { "Tony",19 };
	struct Person p4 = { "Cady",25 };
	struct Person p5 = { "Fudy",24 };
	struct Person p6 = { "Mary",21 };

	//初始化链表
	LinkList myList = initLinkList();
	printf("链表长度为:%d\n", sizeLinkList(myList));
	//插入数据
	insertLinkList(myList, 0, &p1);
	insertLinkList(myList, 0, &p2);
	insertLinkList(myList, -1, &p3);
	insertLinkList(myList, 0, &p4);
	insertLinkList(myList, 1, &p5);
	insertLinkList(myList, 2, &p6);

	//Cady Fudy Mary Judy Andy Tony 

	//遍历链表
	foreachLinkList(myList, myPrintPerson);
	printf("链表长度为:%d\n", sizeLinkList(myList));
	//测试删除按位置
	removeLinkListByPos(myList, 3);
	printf("---------------------\n");
	foreachLinkList(myList, myPrintPerson);
	printf("链表长度为:%d\n", sizeLinkList(myList));
	//测试删除按值
	struct Person p = { "Tony",19 };
	removeLinkListByValue(myList, &p,myComparePerson);
	printf("---------------------\n");
	foreachLinkList(myList, myPrintPerson);
	printf("链表长度为:%d\n", sizeLinkList(myList));
	//测试清空链表
	clearLinkList(myList);
	//返回链表长度
	printf("链表长度为:%d\n", sizeLinkList(myList));
	//销毁链表
	destroyLinkList(myList);
	myList = NULL;
}


int main(void)
{
	test01();

	system("pause");
	return EXIT_SUCCESS;
}