数据结构与算法笔记

2023年1月6号

1.单链表

  • 为什么有链表?

    • 数组查找效率高,但是增删效率低
    • 链表查找效率低,但是增删效率高
  • 链表的组成:数据域、指针域

    • 数据域:存储数据的部分
    • 指针域:连接其他结点
  • 基本操作差不多在学c/c++熟悉了

有头链表

有头单链表,即含有头结点的单向链表

#include<stdio.h>
#include<malloc.h>

struct Node
{
	int data;
	struct Node* next;
};
struct Node* createList()
{
	struct Node* list = (struct Node*)malloc(sizeof(struct Node));
	if (list == NULL)
	{
		return;
	}
	list->next = NULL;
	return list;
};
struct Node* createNode(int data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	if (newNode == NULL)
	{
		return;
	}
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
void insertNode(struct Node* list, int data)
{
	struct Node* newNode = createNode(data);
	newNode->next = list->next;
	list->next = newNode;
}
void printList(struct Node* list)
{
	struct Node* pMove = list->next;
	while (pMove!=NULL)
	{
		printf("%d\t", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}
int main()
{
	struct Node* list = createList();
	for (int i = 0; i < 9; i++)
	{
		insertNode(list, i);
	}
	printList(list);
	return 0;
}

无头链表的第一种写法

  • 无头单链表,即不含有头结点的单项链表
  • 与有头单链表原理一样,但是要注意对于头结点的插入和删除要变更头结点
  • 在面向过程编程时,需要注意函数参数应为二级指针
#include <stdio.h>
#include <stdlib.h>
struct Node 
{
	int data;
	struct Node* next;
};
struct Node* createNode(int data) 
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	if (newNode == NULL)
		return NULL;
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
//再封装的写法
struct list 
{
	struct Node* headNode;		//指向表头的指针
	int listSize;				//万金油
};
//描述最初状态
struct list* createList() 
{
	struct list* myList = (struct list*)malloc(sizeof(struct list));
	if (myList == NULL)
		return NULL;
	myList->headNode = NULL;		
	myList->listSize = 0;
	return myList;
}
//表头法
void insertNode(struct list* myList, int data) 
{
	struct Node* newNode = createNode(data);
	newNode->next = myList->headNode;
	myList->headNode = newNode;
	myList->listSize++;
}
//指定位置插入
void insertNodeAppoin(struct list* myList, int data,int posData)
{
	//指定插入极有可能变成表头插入
	struct Node* posNode = myList->headNode;
	struct Node* preNode = NULL;
	while (posNode != NULL && posNode->data != posData) 
	{
		preNode = posNode;
		posNode = preNode->next;
	}
	if (posNode == NULL) 
	{
		printf("未找到指定位置,无法插入!\n");
	}
	else if (preNode == NULL)	//第一个
	{
		insertNode(myList, data);
	}
	else 
	{
		struct Node* newNode = createNode(data);
		preNode->next = newNode;
		newNode->next = posNode;
		myList->listSize++;
	}
}
//删除! 表头法删除不一样!
void deleteHead(struct list* myList) 
{
	if (myList == NULL || myList->headNode == NULL) 
	{
		printf("error list is NULL\n");
		return;
	}
	struct Node* nextNode=myList->headNode->next;
	free(myList->headNode);
	myList->headNode = nextNode;
	myList->listSize--;
}
//指定未删除也需要考虑是否删除表头
//表尾删除
void printList(struct list* myList) 
{
	struct Node* pMove = myList->headNode;
	while (pMove != NULL) 
	{
		printf("%d\t", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}
int main() 
{
	struct list* myList = createList();
	for (int i = 0; i < 3; i++) 
	{
		insertNode(myList, i);
	}
	printList(myList);
	insertNodeAppoin(myList, -100, 2);
	insertNodeAppoin(myList, -100, 100);
	insertNodeAppoin(myList, -100, 1);
	printList(myList);
	deleteHead(myList);
	printList(myList);
	return 0;
}

无头链表的第二种写法

#include<stdio.h>
#include<stdlib.h>
struct Node
{
	int data;
	struct Node* next;
};
struct Node* createNode(int data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	if (newNode == NULL)
	{
		return;
	}
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
//表头法插入
void insertNodeByFront(struct Node** list,int data)
{
	struct Node* newNode = createNode(data);
	newNode->next = *list;
	*list = newNode;
}
//指定位置插入
void insertNodeByAppoint(struct Node** list, int posData,int data)
{
	struct Node* posNode = *list;
	struct Node* preNode = NULL;
	while (posNode!=NULL&&posNode->data!=posData)
	{
		preNode = posNode;
		posNode = posNode->next;
	}
	if (posNode == NULL)
	{
		printf("链表为空,无法插入\n");
	}
	else if (preNode == NULL)
	{
		insertNodeByFront(list, data);
	}
	else
	{
		struct Node* newNode = createNode(data);
		preNode->next = newNode;
		newNode->next = posNode;
	}
}
//打印
void printNode(struct Node* list)
{
	struct Node* pMove = list;
	while (pMove!=NULL)
	{
		printf("%d\t", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}
int main()
{
	struct Node* list = createNode(1);
	for (int i = 2; i < 4; i++)
	{
		insertNodeByFront(&list, i);
	}
	insertNodeByFront(&list, 8);
	insertNodeByFront(&list, 9);
	insertNodeByAppoint(&list, 1, 5);
	printNode(list);
	return 0;
}

单链表之间的操作

1.找相同的元素组成第三链表,交集

//1.找相同的元素组成第三链表,交集
void intersection(struct Node* listOne, struct Node* listTwo, struct Node* listThree)
{
	for (struct Node* pFirst = listOne->next; pFirst != NULL; pFirst = pFirst->next)
	{
		for (struct Node* pSecond = listTwo->next; pSecond != NULL; pSecond = pSecond->next)
		{
			if (pFirst->data == pSecond->data)
			{
				insertNode(listThree, pFirst->data);
			}
		}
	}
}

2.反转链表

  • 2.1最简单 遍历逆序插入 时间效率低
//释放链表
void freeList(struct Node** list)
{
	if (list == NULL)
	{
		return;
	}
	struct Node* pMove;
	while (*list!=NULL)
	{
		pMove = *list;
		*list = pMove->next;
		free(pMove);
	}
}
//2.反转链表
//2.1最简单 遍历逆序插入 时间效率低
void reverseFirstWay(struct Node** list)
{
	struct Node* tempList = createList();
	struct Node* pMove = (*list)->next;
	while (pMove!=NULL)
	{
		insertNode(tempList, pMove->data);
		pMove = pMove->next;
	}
	freeList(list);
	*list = tempList;
}
  • 反转链表通常用法
//2.2 反转链表通常用法
void reverseSecondWay(struct Node* list)
{
	struct Node* preNode = NULL;
	struct Node* curNode = list->next;
	struct Node* nextNode = list->next;
	while (curNode != NULL)
	{
		nextNode = curNode->next;
		curNode->next = preNode;
		preNode = curNode;
		curNode = nextNode;
	}
	list->next = preNode;
}

有序链表

  • 有序性:插入时保持有序性
#include<stdio.h>
#include<stdlib.h>
/*
* 有序性:插入时保持有序性
*/
struct data
{
	int index;   //构建序号
	int second;
};
struct Node
{
	struct data myData;
	struct Node* next;
};
struct Node* createList()
{
	struct Node* list = (struct Node*)malloc(sizeof(struct Node));
	if (list == NULL)
	{
		return;
	}
	list->next = NULL;
	return list;
}
struct Node* createNode(struct data myData)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	if (newNode == NULL)
	{
		return;
	}
	newNode->next = NULL;
	newNode->myData = myData;
	return newNode;
}
void insertByIndex(struct Node* list, struct data myData)
{
	struct Node* newNode = createNode(myData);
	struct Node* preNode = list;
	struct Node* posNode = list->next;
	while (posNode!=NULL&&posNode->myData.index<myData.index)
	{
		preNode = posNode;
		posNode = posNode->next;
	}
	if (posNode == NULL)
	{
		preNode->next = newNode;
	}
	else
	{
		preNode->next = newNode;
		newNode->next = posNode;
	}
}
int main()
{

	return 0;
}

2.双向链表和双向循环链表

双向链表

  • 双链表的创建
  • 头插法
  • 尾插法
  • 按指定位置删除
  • 表头删除
  • 表尾删除
  • 指定位置删除
#include<stdio.h>
#include<stdlib.h>
//节点属性:单一个体
typedef struct Node
{
	int data;
	struct Node* leftNode;
	struct Node* rightNode;
}NODE,*LPNODE;
//创建节点:把用户的数据变成结构体变量
LPNODE createNode(int data)
{
	LPNODE newNode = (LPNODE)malloc(sizeof(NODE));
	if (newNode == NULL)
	{
		return;
	}
	newNode->data = data;
	newNode->leftNode = NULL;
	newNode->rightNode = NULL;
	return newNode;
}
//再封装写法
//用一个结构体描述链表的特性
typedef struct List
{
	LPNODE headNode;
	LPNODE tailNode;
	int size;
}LIST,*LPLIST;
//创建就是描述最初的状态
LPLIST createList()
{
	LPLIST list = (LPLIST)malloc(sizeof(LIST));
	if (list == NULL)
	{
		return;
	}
	list->size = 0;
	list->headNode = NULL;
	list->tailNode = NULL;
	return list;
}
//头插法
void insertByHead(LPLIST list, int data)
{
	LPNODE newNode = createNode(data);
	if (list->size == 0)
	{
		//list->headNode = newNode;
		list->tailNode = newNode;
	}
	else
	{
		newNode->rightNode = list->headNode;
		list->headNode->leftNode = newNode;
		//list->headNode = newNode;
	}
	list->headNode = newNode;;  //有相同的代码,可以提出来
	list->size++;
}
//尾插法
void insertByTail(LPLIST list, int data)
{
	LPNODE newNode = createNode(data);
	if (list->size == 0)
	{
		list->headNode = newNode;
		//list->tailNode = newNode;
	}
	else
	{
		newNode->leftNode = list->tailNode;
		list->tailNode->rightNode = newNode;
		//list->tailNode = newNode;
	}
	list->tailNode = newNode;  //有相同的代码,可以提出来
	list->size++;
}
//指定位置插入
//防止是头插法
void insertByAppoin(LPLIST list, int data, int posData)
{
	LPNODE posFront = NULL;
	LPNODE posNode = list->headNode;
	while (posNode!=NULL&&posNode->data!=posData)
	{
		posFront = posNode;
		posNode = posNode->rightNode;
	}
	if (posNode == NULL)
	{
		printf("未找到,无法插入\n");
	}
	else
	{
		//如果第一个数就是要找到的
		if (posFront == NULL)
		{
			insertByHead(list, data);    //里面有size++;
		}
		else
		{
			LPNODE newNode = createNode(data);
			posFront->rightNode = newNode;
			newNode->leftNode = posFront;
			newNode->rightNode = posNode;
			posNode->leftNode = newNode;
			list->size++;
		}
	}
}
//表头删除
void deteleByHead(LPLIST list)
{
	LPNODE head = list->headNode;
	list->headNode = head->rightNode;
	list->headNode->leftNode = NULL;
	list->size--;
	free(head);
}
//表尾删除
void deteleByTail(LPLIST list)
{
	LPNODE tail = list->tailNode;
	list->tailNode = tail->leftNode;
	list->tailNode->rightNode = NULL;
	list->size--;
	free(tail);
}
//指定位置删除
void deleteByAppoin(LPLIST list,int data)
{
	LPNODE posFront = list->headNode;
	LPNODE posNode = list->headNode;
	while (posNode!=NULL&&posNode->data!=data)
	{
		posFront = posNode;
		posNode = posNode->rightNode;
	}
	if (posNode == NULL)
	{
		printf("未能找到,无法删除\n");
	}
	else
	{
		if (posFront->data == data)
		{
			deteleByHead(list);
		}
		else
		{
			LPNODE nextNode = posNode->rightNode;
			posFront->rightNode = nextNode;
			nextNode->leftNode = posFront;
			free(posNode);
		}
	}
}
//从表头开始打印
void printPre(LPLIST list)
{
	LPNODE pMove = list->headNode;
	while (pMove != NULL)
	{
		printf("%d\t", pMove->data);
		pMove = pMove->rightNode;
	}
	printf("\n");
}
//从表尾开始打印
void printReverse(LPLIST list)
{
	LPNODE pMove = list->tailNode;
	while (pMove != NULL)
	{
		printf("%d\t", pMove->data);
		pMove = pMove->leftNode;
	}
	printf("\n");
}
int main()
{
	LPLIST list = createList();
	for (int i = 0; i < 5; i++)
	{
		insertByTail(list, i);
	}
	printPre(list);
	printReverse(list);
	//insertByAppoin(list, 99, 0);
	//insertByAppoin(list, 96, 3);
	//printPre(list);
	//printReverse(list);
	deteleByHead(list);
	printPre(list);
	printReverse(list);
	deteleByTail(list);
	printPre(list);
	printReverse(list);
	deleteByAppoin(list, 2);
	printPre(list);
	printReverse(list);
	return 0;
}

双向循环链表

#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
	int data;
	struct Node* leftNode;
	struct Node* rightNode;
}NODE,*LPNODE;
//创建节点
LPNODE createNode(int data)
{
	LPNODE newNode = (LPNODE)malloc(sizeof(NODE));
	if (newNode == NULL)
		return NULL;
	newNode->data = data;
	newNode->leftNode = NULL;
	newNode->rightNode = NULL;
	return newNode;
}
//创建链表
LPNODE createList()
{
	LPNODE headNode = (LPNODE)malloc(sizeof(NODE));
	if (headNode == NULL)
		return NULL;
	headNode->leftNode = headNode;
	headNode->rightNode = headNode;
	return headNode;
}
//尾插法
void insertByTail(LPNODE headNode, int data)
{
	LPNODE newNode = createNode(data);
	headNode->leftNode->rightNode = newNode;
	newNode->leftNode = headNode->leftNode;
	newNode->rightNode = headNode;
	headNode->leftNode = newNode;
}
//打印
//环形,不能以NULL为结束
//顺序打印
void printByRight(LPNODE headNode)
{
	LPNODE pMove = headNode->rightNode;
	while (pMove!= headNode)
	{
		printf("%d\t", pMove->data);
		pMove = pMove->rightNode;
	}
	printf("\n");
}
//逆序打印
void printByLeft(LPNODE headNode)
{
	LPNODE pMove = headNode->leftNode;
	while (pMove != headNode)
	{
		printf("%d\t", pMove->data);
		pMove = pMove->leftNode;
	}
	printf("\n");
}
int main()
{
	LPNODE list = createList();
	for (int i = 0; i < 5; i++)
	{
		insertByTail(list, i);
	}
	printByRight(list);
	return 0;
}

练习

  • 约瑟夫环

  • 两个有序链表序列的交集

  • 武松喝酒景阳冈

3.顺序表(本质是动态数组)

概念部分

顺序表:本质就是动态数组;

  • 对于普通数组,容量是固定的,容易存在空间浪费或空间不足的情况;
int array[10];//只能存10个整数
  • 如果有11个或更多整数需要存储,则会出现空间不足的情况;

  • 如果只有1个整数需要存储,则会出现空间浪费的情况;

为了解决这两种情况,我们可以实现如下结构

int* parray;//指向动态的数组
int len;//记录动态数组的长度
parray = new int[len];//为动态数组开辟空间

通过变量len对指针parray进行操作,实现动态数组

  • 体现有序性

  • 顺序表其实就是线性表的一种,采用顺序存储结构的线性表通常称为顺序表

  • 用一组地址连续的存储单元依次存储线性表的数据元素

数组顺序表

  • 数组存储数据------->数组实现顺序表 数组的有序插入
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX 520

struct MM
{
	char name[20];
	int age;
};
struct Data
{
	int index;
	struct MM mmData;
};
//顺序表结构体
typedef struct sqList
{
	int sqListSize;      //当前顺序表的元素个数
	struct Data* memeory;
}SQL,*LPSQL;
//1.创建顺序表
LPSQL createSqList()
{
	LPSQL sqList = (LPSQL)malloc(sizeof(SQL));
	if (sqList == NULL)
	{
		return NULL;
	}
	sqList->sqListSize = 0;
	sqList->memeory = (struct Data*)malloc(sizeof(struct Data)*MAX);
	if (sqList->memeory == NULL)
		return NULL;
	return sqList;
}
//为数组重新申请内存
void reallocMemeory(LPSQL sqList, int curSize, int newSize)
{
	int max = curSize > newSize ? curSize : newSize;
	sqList->memeory=realloc(sqList->memeory, sizeof(struct Data) * max);
	if (sqList->memeory == NULL)
	{
		return;
	}
}
//2.插入顺序表
void insetData(LPSQL sqList,struct Data data)
{
	//如果顺序表满了,就重新给他申请内存
	if (sqList->sqListSize == MAX)
	{
		reallocMemeory(sqList, MAX, MAX*2);
	}
	//2.1直接插在表尾
	sqList->memeory[sqList->sqListSize] = data;
	//2.2调整位置
	for (int i = sqList->sqListSize; i > 0; i--)
	{
		if (sqList->memeory[i-1].index > sqList->memeory[i].index)
		{
			struct Data temp = sqList->memeory[i];
			sqList->memeory[i] = sqList->memeory[i - 1];
			sqList->memeory[i - 1] = temp;
		}
		else
		{
			break;	
		}
	}
	//2.3元素个数要增加
	sqList->sqListSize++;
}
//3.1删除顺序表
void deleteData(LPSQL sqList, int index)
{
	//找到删除元素的下标
	int pos = -1;
	for (int i = 0; i < sqList->sqListSize; i++)
	{
		if (sqList->memeory[i].index == index)
		{
			pos = i;
			break;
		}
	}
	if (pos == -1)
	{
		printf("未能找到指定元素\n");
	}
	else
	{
		//把数组后面的元素往前移
		for (int i = pos; i < sqList->sqListSize; i++)
		{
			sqList->memeory[i] = sqList->memeory[i + 1];
		}
		sqList->sqListSize--;
		printf("删除成功\n");
	}
}
//3.2删除顺序表的另一种方法   效率更高
void deleteDataSecond(LPSQL sqList,int index)
{
	//找到删除元素的下标
	int pos = -1;
	for (int i = 0; i < sqList->sqListSize; i++)
	{
		if (sqList->memeory[i].index == index)
		{
			pos = i;
			break;
		}
	}
	if (pos == -1)
	{
		printf("未能找到指定元素,删除失败\n");
	}
	else
	{
		int CurSize = sqList->sqListSize;
		//新开内存
		struct Data* newSeq = (struct Data*)malloc(sizeof(struct Data)*(CurSize-1));
		if (newSeq == NULL)
			return;
		//要删除的数据前面的数据拷贝到新开内存
		if (pos != 0)   //前面有
		{
			memcpy(newSeq, sqList->memeory, sizeof(struct Data) * pos);
		}
		else 
		{  
			       //前面没有
		}
		//要删除的数据后面的数据拷贝到新开内存
		if (pos != (CurSize - 1))
		{
			memcpy(newSeq + pos, sqList->memeory + pos + 1, sizeof(struct Data) * (CurSize - 1 - pos));

		}
		//释放内存
		free(sqList->memeory);
		sqList->memeory = newSeq;
		sqList->sqListSize--;
	}
}
//4.遍历顺序表
void printSqlist(LPSQL sqList)
{
	for (int i = 0; i < sqList->sqListSize; i++)
	{
		printf("%d\t%s\t%d\n", sqList->memeory[i].index,
			sqList->memeory[i].mmData.name,
			sqList->memeory[i].mmData.age);
	}
}

int main()
{
	LPSQL sqList = createSqList();
	struct Data array[5] = {5,"小王",21,3,"小李",18, 7,"小吕",9, 2,"小红",18, 99,"小薯",21};
	for (int i = 0; i < 5; i++)
	{
		insetData(sqList, array[i]);
	}
	//deleteData(sqList, 99);
	printSqlist(sqList);
	printf("删除后\n");
	deleteDataSecond(sqList,99);
	printSqlist(sqList);
	return 0;
}

链式顺序表

  • 链式结构储存------->链表实现排序 有序链表的构建

  • 把插入方式改成指定位置插入

    比如 1 2 3 5

    4:找到第一次大于4的位置,插到中间,也就是3和5的位置

#include <stdio.h>
#include <stdlib.h>
struct MM 
{
	char name[20];
	int age;
};
struct Data 
{
	int index;
	struct MM mmData;
};

typedef struct Node
{
	struct Data data;
	struct Node* next;
}LIST,*LPLIST;
LPLIST createList()
{
	LPLIST headNode = (LPLIST)malloc(sizeof(LIST));
	if (headNode == NULL)
		return NULL;
	headNode->next = NULL;
	return headNode;
}
LPLIST createNode(struct Data data)
{
	LPLIST newNode = (LPLIST)malloc(sizeof(LIST));
	if (newNode == NULL)
		return NULL;
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}

typedef struct sqList 
{
	struct Node* headNode;
	int sqListSize;
}SQL,*LPSQL;

LPSQL createSqList() 
{
	LPSQL sqList = (LPSQL)malloc(sizeof(SQL));
	if (sqList == NULL)
		return NULL;
	sqList->sqListSize = 0;
	sqList->headNode = createList();
	return sqList;
}
void insertData(LPSQL sqList, struct Data data) 
{
	LPLIST newNode = createNode(data);
	//找指定位置
	LPLIST leftPos = sqList->headNode;
	LPLIST posNode = sqList->headNode->next;
	while (posNode != NULL && posNode->data.index < data.index) 
	{
		leftPos = posNode;
		posNode = leftPos->next;
	}
	leftPos->next = newNode;
	newNode->next = posNode;   //posNode为NULL newNode就是最后一个节点
	sqList->sqListSize++;
}
void deleteNode(LPSQL sqList, int index) 
{
	LPLIST leftPos = sqList->headNode;
	LPLIST posNode = sqList->headNode->next;
	while (posNode != NULL && posNode->data.index != index) 
	{
		leftPos = posNode;
		posNode = leftPos->next;
	}
	if (posNode == NULL)
	{
		printf("删除失败!未找到\n");
		return;
	}
	else 
	{
		leftPos->next = posNode->next;
		free(posNode);
		posNode = NULL;
		sqList->sqListSize--;
	}
}

void traverseSqList(LPSQL sqList)
{
	LPLIST pMove = sqList->headNode->next;
	while (pMove != NULL) 
	{
		printf("%d:%s\t%d\n", pMove->data.index, pMove->data.mmData.name, pMove->data.mmData.age);
		pMove = pMove->next;
	}
}
void deleteSqList(LPSQL sqList)
{
	while (sqList->headNode != NULL) 
	{
		LPLIST nextNode = sqList->headNode->next;
		free(sqList->headNode);
		sqList->headNode = nextNode;
	}
	sqList->sqListSize = 0;
}

int main() 
{
	LPSQL sqList = createSqList();
	struct Data array[4] = { 4,"yw",18, 1,"wd",27 ,3,"云墨",29 ,2,"yykk",25 };
	for (int i = 0; i < 4; i++)
	{
		insertData(sqList, array[i]);
	}
	traverseSqList(sqList);
	deleteNode(sqList, 3);
	printf("\n");
	traverseSqList(sqList);
	deleteSqList(sqList);
	free(sqList);
	return 0;
}

4.数据结构之串

  • 串(string):由零个或多个字符组成的有限序列,也称字符串

串的定义实现和基本操作

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 1024
typedef struct string
{
	char memery[MAXSIZE];
	int curSize;           //万金油参数
}string,*LPSTR;
//创建一个串
LPSTR createStr(const char* str)
{
	LPSTR pStr = (LPSTR)malloc(sizeof(string));
	if (pStr == NULL)
	{
		return;
	}
	for (int i = 0; i < MAXSIZE; i++)
	{
		pStr->memery[i] = '\0';
	}
	int count = 0;
	//赋值
	while (str[count] != '\0')
	{
		pStr->memery[count] = str[count];
		count++;
	}
	pStr->curSize=count;
}
//串的插入  在当前串中插入一个字符串
//123456
//插入abc
//abc123456
//1abc23456
//123456abc
//          要插在哪个串  插入的字符串   插入串的长度  插入的位置 
void insertStr(LPSTR pStr, const char* str, int len, int pos)
{
	if (pos<0 || pos>MAXSIZE|| pos > pStr->curSize)
	{
		printf("下标有误,无法插入\n");
		return;
	}
	if (pStr->curSize + len > MAXSIZE)
	{
		printf("插入的字符串太长,无法插入\n");
		return;
	}
	//如果插入的位置等于当前串的最后面
	if (pos == pStr->curSize)
	{
		for (int i = 0; i < len; i++)
		{
			pStr->memery[pStr->curSize++] = str[i];
		}
	}
	else
	{
		//1.腾位置
		for (int i = pStr->curSize; i >= pos; i--)
		{
			pStr->memery[len + i] = pStr->memery[i];
		}
		//2.插入字符串
		for (int i = 0; i < len; i++)
		{
			pStr->memery[pos + i] = str[i];
		}
		pStr->curSize += len;
	}
}
//串的删除
//按区间删除
void deletepStr(LPSTR pStr, int star, int end)
{
	if (star > end || end > pStr->curSize || star < 0)
	{
		printf("区间错误,无法删除\n");
		return;
	}
	int count = star - end + 1;    //删除字符串的长度
	//把后面的字符串往前移
	for (int i = end, j = star - 1; i < pStr->curSize; i++, j++)
	{
		pStr->memery[j] = pStr->memery[i];
	}
	//把后面的置空
	for (int i = pStr->curSize; i > pStr->curSize - count; i--)
	{
		pStr->memery[i] = '\0';
	}
	pStr->curSize -= count;
}
//串的打印
void printStr(LPSTR pStr)
{
	for (int i = 0; i < pStr->curSize; i++)
	{
		printf("%c", pStr->memery[i]);
	}
	printf("\n");
	//puts(pStr);   也可以这个
	// %s           也可以这个
}
int main()
{
	LPSTR pStr = createStr("123456");
	printStr(pStr);
	insertStr(pStr, "abc", 3, 6);
	printStr(pStr);
	deletepStr(pStr, 1, 3);
	printStr(pStr);
	return 0;
}

BF算法(暴力匹配)

BF算法过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Ga61qR2-1693128614658)(E:\image-20230114004008915.png)]

int BF(LPSTR pStr1, LPSTR pStr2)
{
	int i = 0;
	int j = 0;
	int index;
	while (pStr1->memery[i] != '\0' || pStr2->memery[j] != '\0')
	{
		if (pStr1->memery[i] == pStr2->memery[j])
		{
			i++;
			j++;
		}
		else
		{
			index++;
			i = index;
			j = 0;
		}
	}
	if (pStr2->memery[j] = '\0')
	{
		return index + 1;
	}
}

KPM算法(串匹配算法)

  • 我太菜了,现在还不会

5.数据结构之栈

  • 主要用于有回退思想的场景中

栈:FILO 先进后出

  • 1.入栈
  • 出栈
  • 获取栈元素

数组栈

#include <stdio.h>
#include <stdlib.h>
#define MAX 100
typedef int TYPE;

//栈的结构体
typedef struct stack
{
	TYPE* memory;	//栈的容量
	int top;		//栈顶标记
}STACK, * LPSTACK;

//1.创建一个栈
LPSTACK createStack()
{
	LPSTACK pStack = (LPSTACK)malloc(sizeof(STACK));
	if (pStack == NULL)
	{
		return NULL;
	}
	pStack->memory = (TYPE*)malloc(sizeof(TYPE) * MAX);
	pStack->top = -1;
	return pStack;
}
//2.入栈操作
void push(LPSTACK stack, TYPE data)
{
	//细节
	if (stack == NULL || stack->top >= (MAX - 1))
	{
		return;
	}
	stack->memory[++stack->top] = data;
}
//3.出栈操作
void pop(LPSTACK stack)
{
	if (stack == NULL || stack->top == -1)
	{
		return;
	}
	stack->top--;		//栈顶标记往0靠拢
}
//4.获取栈顶元素
TYPE getTop(LPSTACK stack)
{
	return stack->memory[stack->top];
}
//书本上:可能会把3和4综合起来
void pop2(LPSTACK stack, TYPE* data)
{
	if (stack != NULL && stack->top != -1)
	{
		*data = stack->memory[stack->top--];
	}
}
int empty(LPSTACK stack)
{
	return stack->top == -1;		//1表示NULL
}

int main()
{
	LPSTACK stack = createStack();
	for (int i = 0; i < 3; i++)
	{
		push(stack, i);
	}
	//FILO  回退思想
	while (!empty(stack))
	{
		printf("%d\t", getTop(stack));
		pop(stack);
	}
	return 0;
}

双端栈

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 10
typedef struct 
{
	int* memory;
	int stackTop[2];
}STACK,*LPSTACK;
enum FLAG {left,right};
LPSTACK  creatStack() 
{
	LPSTACK pStack = (LPSTACK)malloc(sizeof(STACK));
	if (pStack == NULL)
		return NULL;
	pStack->memory = (int*)malloc(sizeof(int)*MAX);
	pStack->stackTop[0] = -1;
	pStack->stackTop[1] = MAX;
	return pStack;
}
void push(LPSTACK stack, int data, int flag)
{
	if (stack->stackTop[0] + 1 == stack->stackTop[1]) 
	{
		printf("满了!");
		return;
	}
	switch (flag) 
	{
	case 0:
		stack->memory[++stack->stackTop[0]] = data;
		break;
	case 1:
		stack->memory[--stack->stackTop[1]] = data;
		break;
	}
}
void pop(LPSTACK stack, int* data, int flag)
{
	switch(flag)
	{
	case 0:
		if (stack->stackTop[0] == -1) 
		{
			return;
		}
		*data = stack->memory[stack->stackTop[0]--];
		break;
	case 1:
		if (stack->stackTop[1] == MAX) 
		{
			return;
		}
		*data = stack->memory[stack->stackTop[1]++];
		break;
	}
}
int main() 
{
	srand((unsigned int)time(NULL));
	LPSTACK stack = creatStack();
	for (int i = 0; i < 10; i++) 
	{
		push(stack, i, rand() % 2);
	}
	//出栈
	int data = 0;
	while (stack->stackTop[0] != -1) 
	{
		pop(stack, &data, left);
		printf("%d\t", data);
	}
	printf("\n");
	while (stack->stackTop[1] != MAX)
	{
		pop(stack, &data, right);
		printf("%d\t", data);
	}
	printf("\n");
	return 0;
}

链式栈

/*
	链式栈:
		3 2 1
		1 2 3
	 //链表的表头法插入
		入栈: 表头插入
		1
		2 1
		3 2 1
		出栈: 表头删除
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct Node 
{
	int data;
	struct Node* next;
}NODE,*LPNODE;
LPNODE createNode(int data) 
{
	LPNODE newNode = (LPNODE)malloc(sizeof(NODE));
	if (newNode == NULL)
		return NULL;
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
//单独用个结构描述栈结构
typedef struct 
{
	LPNODE stackTop;
	int size;
}STACK,*LPSTACK;
LPSTACK createStack() 
{
	LPSTACK pStack = (LPSTACK)malloc(sizeof(STACK));
	if (pStack == NULL)
		return NULL;
	pStack->stackTop = NULL;
	pStack->size = 0;
	return pStack;
}
void push(LPSTACK stack, int data) 
{
	LPNODE newNode = createNode(data);
	//无头链表的表头法插入
	newNode->next = stack->stackTop;
	stack->stackTop = newNode;
	stack->size++;
}
int top(LPSTACK stack) 
{
	if (stack->size != 0)
		return stack->stackTop->data;
	return INT_MAX;
}
void pop(LPSTACK stack) 
{
	if (stack->size != 0) 
	{
		LPNODE nextNode = stack->stackTop->next;
		free(stack->stackTop);
		stack->stackTop = nextNode;
		stack->size--;
	}
}
int empty(LPSTACK stack) 
{
	return stack->size == 0;
}
int main() 
{
	LPSTACK stack = createStack();
	for (int i = 0; i < 3; i++)
	{
		push(stack, i);
	}
	//FILO  回退思想
	while (!empty(stack))
	{
		printf("%d\t", top(stack));
		pop(stack);
	}
	return 0;
}

练习

回退思想 FILE

  • 进制转换
#include<stdio.h>
#include<stdlib.h>
#include<stack>
int main()
{
	//准备栈
	int stack[100];
	int top = -1;

	int num = 123;
	while (num != 0)
	{
		//入栈
		stack[++top] = num % 2;
		num /= 2;
	}
	//出栈与获取栈顶元素
	while (top != -1)
	{
		printf("%d", stack[top--]);
	}
	printf("\n");

	//用STL容器里面的栈
	std::stack<int> mystack;
	num = 123;
	while (num != 0)
	{
		mystack.push(num % 2);
		num /= 2;
	}
	while (!mystack.empty())
	{
		printf("%d", mystack.top());
		mystack.pop();
	}
	return 0;
}
  • 括号匹配
//
// 括号匹配问题
// (iugawd)(liwad)(
// 左:入栈
// 右:出栈
//
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int match(char* str)
{
	//栈
	char stackMemroy[1024];
	int top = -1;

	int i = 0;
	while (str[i] != '\0')
	{
		if (str[i] == ')')
		{
			if (top > -1)
			{
				top--;
			}
			else
			{
				return -1;
			}
		}
		else if (str[i] == '(')
		{
			stackMemroy[++top] = str[i];
		}
		i++;
	}
	if (top == -1)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}
int main()
{
	char str[1024] = "";
	gets_s(str, 1024);
	int result = match(str);
	if (result == 0)
	{
		printf("匹配\n");
	}
	else if (result == 1)
	{
		printf("多了左边括号");
	}
	else
	{
		printf("多了右边括号");
	}
	return 0;
}
  • 迷宫寻路过程:回退思想

    不考虑最短路径

#include<stdio.h>
#include<stdlib.h>
struct position
{
	int row;
	int cols;
};
//准备栈
struct position patnStack[100];
int stackTop = -1;

//迷宫属性
int** maze = NULL;
int size = 0;
int** createArray(int row, int cols)
{
	int** array = (int**)malloc(sizeof(int*)*row);
	for (int i = 0; i < row; i++)
	{
		array[i] = (int*)malloc(sizeof(int)*cols);
	}
	return array;
}
void createMaze()
{
	printf("迷宫大小:");
	scanf("%d", &size);
	printf("请输入地图:\n");
	maze = createArray(size + 2, size + 2);
	for (int i = 1; i <= size; i++)
	{
		for (int j = 1; j <= size; j++)
		{
			scanf("%d", &maze[i][j]);
		}
	}
	//给迷宫围上墙
	for (int i = 0; i <= size + 1; i++)
	{
		maze[0][i] = maze[size + 1][i] = 1; //第一行和最后一行     
		maze[i][0] = maze[i][size + 1] = 1; //第一列和最后一列
	}
}
int findPath()
{
	//准备位移变化
	struct position offset[4];    //准备四个方向移动
	//往右走
	offset[0].row = 0;
	offset[0].cols = 1;
	//往下走
	offset[1].row = 1;
	offset[1].cols = 0;
	//往左走
	offset[2].row = 0;
	offset[2].cols = -1;
	//往上走
	offset[3].row = -1;
	offset[3].cols = 0;
	//选定入口
	struct position here = { 1,1 };
	maze[1][1] = 1;    //堵起来
	int option = 0;    //起始方向右边
	int endOption = 3; //终止方向
	while (here.row != size || here.cols != size)
	{
		int rowNum, colsNum;   //记录变化
		//试探
		while (option <= endOption)
		{
			//行变化:原位置+偏移量
			rowNum = here.row + offset[option].row;
			colsNum = here.cols + offset[option].cols;
			//当试探出一条路,直接走完要入栈
			if (maze[rowNum][colsNum] == 0)
			{
				break;
			}
			option++;     //不能走的话,试探下一个方式
		}
		//能找到能走的路
		if (option <= endOption)
		{
			//走到下一个,上一个位置入栈
			patnStack[++stackTop] = here;
			//走到下一个
			here.row = rowNum;
			here.cols = colsNum;
			//堵路
			maze[rowNum][colsNum] = 1;
			option = 0;   //重新试探
		}
		else
		{
			if (stackTop == -1)
			{
				return 0;
			}
			//出栈
			struct position next = patnStack[stackTop--];
			//方向的处理:要去的方向改为原来试探过的方向的下一个
			//单纯的提高效率
			if (next.row == here.row)   //行不变,左右走
			{
				//1 3
				option = 2 + next.cols - here.cols;
			}
			else
			{
				option = 3 + next.row - here.row;
			}
			here = next;
		}
	}
	return 1;
}
void printPath()
{
	printf("路径:\n");
	struct position curPos;
	while (stackTop != -1)
	{
		curPos = patnStack[stackTop--];
		printf("(%d,%d)--->", curPos.row, curPos.cols);
	}
	printf("\n");
}
int main()
{
	createMaze();
	if (findPath())
	{
		printPath();
	}
	else
	{
		printf("没有找到路径\n");
	}
	return 0;
}

6.队列

队列 FIFO:先进先出

有一个头 front

有一个尾 tail

数组队列

  • 数组队列是一次性队列,会出现伪溢出,要想多次使用,需要使用循环队列或者链式队列
#include<stdio.h>
#include<stdlib.h>
#define MAX 10
typedef struct
{
	int* queueMenmory;
	int front;
	int tail;
	int size;
}QUEUE,*LPQUEUE;
LPQUEUE createQueue()
{
	LPQUEUE pQ = (LPQUEUE)malloc(sizeof(QUEUE));
	if (pQ == NULL)
		return;
	pQ->queueMenmory = (int*)malloc(sizeof(int) * MAX);
	pQ->size = 0;
	pQ->front = 0;
	pQ->tail = 0;
	return pQ;
}
//入队操作
void push(LPQUEUE queue, int data)
{
	if (queue == NULL || queue->tail >= MAX)
	{
		return;
	}
	queue->queueMenmory[queue->tail++] = data;
	queue->size++;
}
//出队操作
void pop(LPQUEUE queue)
{
	if (queue->front < queue->tail)
	{
		queue->front++;
		queue->size--;
	}
}
int front(LPQUEUE queue)
{
	return queue->queueMenmory[queue->front];
}
int empty(LPQUEUE queue)
{
	return queue->size == 0;
}
int main()
{
	//一次性队列
	LPQUEUE queue = createQueue();
	for (int i = 1; i <= 3; i++)
	{
		push(queue, i);
	}
	while (!empty(queue))
	{
		printf("%d\t", front(queue));
		pop(queue);
	}
	printf("\n");
	//伪溢出
	for (int i = 1; i <= 10; i++)
	{
		push(queue, i);
	}
	while (!empty(queue))
	{
		printf("%d\t", front(queue));
		pop(queue);
	}
	return 0;
}

循环队列

  • 可以多次使用的队列,不存在有伪溢出
  • 要求余(%MAX)
#include<stdio.h>
#include<stdlib.h>
#define MAX 10
typedef struct
{
	int* memory;
	int front;
	int tail;
	int szie;
}QUEUE,*LPQUEUE;
LPQUEUE createQueue()
{
	LPQUEUE queue = (LPQUEUE)malloc(sizeof(QUEUE));
	if (queue == NULL)
		return;
	queue->memory = (int*)malloc(sizeof(int) * MAX);
	queue->szie = 0;
	queue->front = 0;
	queue->tail = 0;
	return queue;
}
void push(LPQUEUE queue,int data)
{
	if (queue->szie == MAX)
		return;
	queue->memory[queue->tail % MAX] = data;
	queue->tail = (queue->tail + 1) % MAX;
	queue->szie++;
}
void pop(LPQUEUE queue)
{
	if (queue->szie != 0)
	{
		queue->front=(queue->front + 1) % MAX;
		queue->szie--;
	}
}
int front(LPQUEUE queue)
{
	return queue->memory[queue->front % MAX];
}
int empty(LPQUEUE queue)
{
	return queue->szie == 0;
}
int main()
{
	LPQUEUE queue = createQueue();
	for (int i = 1; i <= 3; i++)
	{
		push(queue, i);
	}
	while (!empty(queue))
	{
		printf("%d\t", front(queue));
		pop(queue);
	}
	printf("\n");
	for (int i = 1; i <= 10; i++)
	{
		push(queue, i);
	}
	while (!empty(queue))
	{
		printf("%d\t", front(queue));
		pop(queue);
	}
	return 0;
}

链式队列

  • 无表头链表
  • 记录表头和表尾的位置去操作链式结点
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
	int data;
	struct Node* next;
}NODE,*LPNODE;
LPNODE createNode(int data)
{
	LPNODE newNode = (LPNODE)malloc(sizeof(NODE));
	if (newNode == NULL)
		return;
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
typedef struct
{
	int queueSize;
	LPNODE frontNode;  //队头
	LPNODE tailNode;   //队尾
}QUEUE,*LPQUEUE;
LPNODE createQueue()
{
	LPQUEUE queue = (LPQUEUE)malloc(sizeof(QUEUE));
	if (queue == NULL)
		return;
	queue->frontNode = NULL;
	queue->tailNode = NULL;
	queue->queueSize = 0;
	return queue;
}
//无头链表的表尾法插入
void push(LPQUEUE queue, int data)
{
	LPNODE newNode = createNode(data);
	if (queue->queueSize == 0)
	{
		queue->frontNode = newNode;
		queue->tailNode = newNode;
	}
	else
	{
		queue->tailNode->next = newNode;
		queue->tailNode = newNode;
	}
	queue->queueSize++;
}
//出队:表头法删除
void pop(LPQUEUE queue)
{
	if (queue->queueSize != 0)
	{
		LPNODE nextNode = queue->frontNode->next;
		free(queue->frontNode);
		queue->frontNode = nextNode;
		queue->queueSize--;
	}
}
int front(LPQUEUE queue)
{
	return queue->frontNode->data;
}
int empty(LPQUEUE queue)
{
	return queue->queueSize == 0;
}
void deleteQueue(LPQUEUE queue)
{
	while (!empty(queue))
	{
		pop(queue);
	}
	free(queue);
	queue = NULL;
}
int main()
{
	LPQUEUE queue = createQueue();
	for (int i = 1; i <= 3; i++)
	{
		push(queue, i);
	}
	while (!empty(queue))
	{
		printf("%d\t", front(queue));
		pop(queue);
	}
	printf("\n");
	for (int i = 1; i <= 10; i++)
	{
		push(queue, i);
	}
	while (!empty(queue))
	{
		printf("%d\t", front(queue));
		pop(queue);
	}
	return 0;
}

优先队列

  • 优先级
  • 以短作业优先法为例
/*
*  短作业
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MaxQueueSize 100
typedef int ElementType;
typedef struct
{
	int priority;			//工作量
	ElementType element;	//队列中的元素
}DataType;

typedef struct
{
	int size;
	DataType memory[MaxQueueSize];
}PQ, * LPPQ;
LPPQ createQueue()
{
	LPPQ queue = (LPPQ)malloc(sizeof(PQ));
	memset(queue->memory, 0, MaxQueueSize * sizeof(DataType));
	queue->size = 0;
	return queue;
}
int empty(LPPQ queue)
{
	if (queue->size <= 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
void push(LPPQ queue, DataType data)
{
	if (queue->size >= MaxQueueSize)
	{
		return;
	}
	else
	{
		queue->memory[queue->size++] = data;
	}
}
void pop(LPPQ queue, DataType* data)
{
	DataType min;
	int minIndex = 0;
	if (queue->size <= 0)
	{
		return;
	}
	else
	{
		min = queue->memory[0];
		for (int i = 1; i < queue->size; i++)
		{
			//当前作业量 小于原来的
			if (queue->memory[i].priority < min.priority)
			{
				min = queue->memory[i];
				minIndex = i;
			}
		}
		*data = queue->memory[minIndex];
		//调整数组
		for (int i = minIndex + 1; i < queue->size; i++)
		{
			queue->memory[i - 1] = queue->memory[i];
			minIndex = i;
		}
		queue->size--;
	}
}
int main()
{
	LPPQ queue = createQueue();
	DataType tempData;
	FILE* fp = fopen("task.txt", "r");
	while (!feof(fp))
	{
		fscanf(fp, "%d\t%d\n", &tempData.element, &tempData.priority);
		push(queue, tempData);
	}
	int i = 1;
	printf("序号\t任务\t优先级\n");
	while (!empty(queue))
	{
		pop(queue, &tempData);
		printf("%d\t%d\t%d\n", i, tempData.element, tempData.priority);
		i++;
	}
	printf("\n");
	return 0;
}

7.hash

hash 基础

  • 1.hash叫做哈希,又称为散列 h(k)=2*k

  • 2.hash函数

    • 1.1 取余法(用的比较多)

      h(k)=k%p

      p=10

      h(10)=10%10=0;

      h(14)=14%10=4;

      h(15)=15%10=5;

      Hash[0]=10;

      Hash[4]=14;

      Hash[5]=15;

    • 1.2 直接定值

      h(k)=k;

      12 13 1 10000

      array[12]=12;

      array[13]=13

    • 随机数法

  • 3.随机储存结构

  • 4.hash中比较重要的两个概念:

    • 4.1 hash构造函数
    • 4.2 hash冲突
      • 处理hash冲突的方法: 1.开放地址法:线性探测 2.链表法 3.在散列法

数组法描述hash函数

8.大顶堆

  • 基于完全二叉树
  • 1.每次插入元素,放在curSize 下标下
  • 2.需要判定当前位置是否适合,找到他适合的位置(通过逻辑树的方式减少比较次数)
  • 3.每个结点的对应的值都大于孩子结点对应的值,这样的逻辑结构叫做大根堆,也叫大顶堆
  • 4.根结点的下标/2 == 父结点的下标
  • 5.父结点下标*2 == 左孩子下标 父结点下标*2+1 == 右孩子下标

创建堆

typedef struct
{
	int sizeHeap;     //堆中当前元素
	int* heapData;    //数组描述
}HEAP,*LPHEAP;
//创建堆
LPHEAP createHeap()
{
	LPHEAP heap = (LPHEAP)malloc(sizeof(HEAP));
	if (heap == NULL)
		return;
	heap->sizeHeap = 0;
	heap->heapData = (int*)malloc(sizeof(int) * MAX);
	return heap;
}

调整堆成为大顶堆

//调整一下
void moveToCreectPos(LPHEAP heap, int curPos)
{
	while (curPos > 1)
	{
		int Max = heap->heapData[curPos];    //假设最大值为最后一个节点
		int parentIndex = curPos / 2;     //跟当前节点的父节点比较
		if (Max > heap->heapData[parentIndex])
		{
			heap->heapData[curPos] = heap->heapData[parentIndex];
			heap->heapData[parentIndex] = Max;
			curPos = parentIndex;
		}
		else
		{
			break;      //当元素小于父节点,满足要求,无需调整
		}
	}
}

堆排序:有序性的出堆

//堆排序:有序性的出堆
int popMax(LPHEAP heap)
{
	int Max = heap->heapData[1];   //最大
	//调整堆
	int curPos = 1;
	int childPos = curPos * 2;
	while (childPos <= heap->sizeHeap)
	{
		int temp = heap->heapData[childPos];
		//同一层上进行左右比较
		if (childPos + 1 <= heap->sizeHeap && temp < heap->heapData[childPos + 1])
		{
			temp = heap->heapData[++childPos];
		}
		//向下寻找
		heap->heapData[curPos] = temp;
		curPos = childPos;
		childPos *= 2;
	}
	heap->heapData[curPos] = heap->heapData[heap->sizeHeap];
	--heap->sizeHeap;
	return Max;
}

综合代码

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MAX 11
typedef struct
{
	int sizeHeap;     //堆中当前元素
	int* heapData;    //数组描述
}HEAP,*LPHEAP;
//创建堆
LPHEAP createHeap()
{
	LPHEAP heap = (LPHEAP)malloc(sizeof(HEAP));
	if (heap == NULL)
		return;
	heap->sizeHeap = 0;
	heap->heapData = (int*)malloc(sizeof(int) * MAX);
	return heap;
}
//万金油函数
int size(LPHEAP heap)
{
	return heap->sizeHeap;
}
int empty(LPHEAP heap)
{
	return heap->sizeHeap == 0;
}
//调整一下
void moveToCreectPos(LPHEAP heap, int curPos)
{
	while (curPos > 1)
	{
		int Max = heap->heapData[curPos];    //假设最大值为最后一个节点
		int parentIndex = curPos / 2;     //跟当前节点的父节点比较
		if (Max > heap->heapData[parentIndex])
		{
			heap->heapData[curPos] = heap->heapData[parentIndex];
			heap->heapData[parentIndex] = Max;
			curPos = parentIndex;
		}
		else
		{
			break;      //当元素小于父节点,满足要求,无需调整
		}
	}
}
void insertHeap(LPHEAP heap, int data)
{
	//判断是否已满
	if (heap->sizeHeap == MAX - 1)
	{
		return;
	}
	heap->heapData[++heap->sizeHeap] = data;
	//大顶堆:去调整
	moveToCreectPos(heap, heap->sizeHeap);
}
void printHeap(LPHEAP heap)
{
	for (int i = 1; i < heap->sizeHeap+1; i++)
	{
		printf("%d\t", heap->heapData[i]);
	}
	printf("\n");
}
//堆排序:有序性的出堆
int popMax(LPHEAP heap)
{
	int Max = heap->heapData[1];   //最大
	//调整堆
	int curPos = 1;
	int childPos = curPos * 2;
	while (childPos <= heap->sizeHeap)
	{
		int temp = heap->heapData[childPos];
		//同一层上进行左右比较
		if (childPos + 1 <= heap->sizeHeap && temp < heap->heapData[childPos + 1])
		{
			temp = heap->heapData[++childPos];
		}
		//向下寻找
		heap->heapData[curPos] = temp;
		curPos = childPos;
		childPos *= 2;
	}
	heap->heapData[curPos] = heap->heapData[heap->sizeHeap];
	--heap->sizeHeap;
	return Max;
}
int main()
{
	srand((unsigned int)time(NULL));
	LPHEAP heap = createHeap();
	for (int i = 0; i < 10; i++)
	{
		insertHeap(heap, rand() % 100);
	}
	printf("储存情况:\n");
	printHeap(heap);
	printf("堆排序:\n");
	while (!empty(heap))
	{
		printf("%d\t", popMax(heap));
	}
	return 0;
}

9.二叉树基础知识

二叉树基础

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。

根结点:根结点没有前驱结点

二叉树的定义:二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成

二叉树的特点:每个结点的度只可能是0,1,2;二叉树是有序树,即使某结点只有一棵子树,也要区分该子树是左子树还是右子树。

树是递归定义的

  • 1.结点的度:一个结点含有的子树个数被称为该结点的度

  • 2.叶结点:度为0的结点称为叶结点

  • 3.兄弟结点:具有相同父结点的结点互称为兄弟结点

  • 4.树的度:一棵树中,最大的结点称为树的度

  • 5.结点的层次:从根开始定义起,根为第一层,根的子结点为第二层,以此类推…

  • 6.树的高度或深度:树中结点的最大层次

  • 7.树的广度:一个结点有多少根线就是多少度

  • 8.路径:如果树的结点序列n1, n2, …, nk有如下关系:结点ni是ni+1的双亲(1<=i<k),则把n1, n2, …, nk称为一条由n1至nk的路径;路径上经过的边的个数称为路径长度。

  • 9.祖先、子孙:在树中,如果有一条路径从结点x到结点y,那么x就称为y的祖先,而y称为x的子孙。

  • 10.结点所在层数:根结点的层数为1;对其余任何结点,若某结点在第i层,则其孩子结点在第i+1层。

  • 11.有序树、无序树:如果一棵树中结点的各子树从左到右是有次序的,称这棵树为有序树;反之,称为无序树。

    • 数据结构中讨论的一般都是有序树
  • 12.森林:m (m≥0)棵互不相交的树的集合。

满二叉树

在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上。

  • 满二叉树的特点:

    • 1.叶子只能出现在最下一层;

    • 2.只有度为0和度为2的结点。

完全二叉树

对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中的位置完全相同。

  • 完全二叉树的特点

      1. 叶子结点只能出现在最下两层,且最下层的叶子结点都集中在二叉树的左部;
      1. 完全二叉树中如果有度为1的结点,只可能有一个,且该结点只有左孩子。
      1. 深度为k的完全二叉树在k-1层上一定是满二叉树。

因此,满二叉树一定是完全二叉树,但是完全二叉树不一定是满二叉树

二叉树的性质

  • 性质1 :二叉树的第i层上最多有2i-1个结点(i≥1)。

    • 推广:深度为h的k叉树中,第i层上最多具有ki-1个结点。
  • 性质2:一棵深度为k的二叉树中,最多有2k-1个结点,最少有k个结点。深度为k且具有2k-1个结点的二叉树一定是满二叉树,深度为k且具有k个结点的二叉树不一定是斜树。

  • 性质3:在一棵二叉树中,如果叶子结点数为n0,度为2的结点数为n2,则有: n0=n2+1。

    • 证明: 抓住结点总数=结点总度数+1

      n0+n1+n2=n1+2*n2+1 n0=n2 +1

  • 推广:已知一棵树度为m的树中有n1个度为1的结点,n2个度为2的结点,…nm个度为m的结点,问该树中有多少片叶子?

    • 证明:根据结点总数=结点总度数+1

      n0+n1+n2+…+nm=n1+2*n2+…+m*nm+1

      => n0=1+n2+…+(m-1)nm

  • 性质4 具有n个结点的完全二叉树的深度为 log(2)n +1。

    • 证明:假设具有n个结点的完全二叉树的深度为k,根据完全二叉树的定义和性质2,有下式成立

      2k-1 ≤ n < 2k

树的存储结构

  • 实现树的存储结构,关键是什么?

​ 如何表示树中结点之间的逻辑关系。

  • 存储问题的出发点

​ 如何表示结点的双亲和孩子

  • 多叉链表表示法二叉树的二叉链表结构采用两个指针域存储结点可能有的孩子指针。树的多叉链表表示法延伸了这种结构设计:若树的度为K,则在结点结构中设置K个孩子指针域,使所有结点同构。

  • 双亲表示法以一组连续空间存储树的结点,在每个结点中设一个指示器指示双亲结点的位置

  • 孩子链表表示法每个结点的孩子以单链表的形式存储,n个结点有n个孩子链表,n个头指针又组成一个线性表,并以顺序存储结构存储。

  • 孩子兄弟表示法以二叉链表作为树的存储结构,链表中的结点的两个指针分别指向该结点的第一个孩子结点和下一个兄弟结点。

10.二叉树的创建与遍历

呆比法创建法

就是单纯的将一个个结点连接起来,所以叫呆比创建法

#include<stdio.h>
#include<stdlib.h>
//1.结构体描述
typedef struct Node
{
	char data;               //数据
	struct Node* Lchild;     //左子树指针
	struct Node* Rchild;     //右子树指针
}TREE,*LPTREE;
//2.呆比法创建法
//把数据变成一个结点
LPTREE createNode(char data)
{
	LPTREE newNode = (LPTREE)malloc(sizeof(TREE));
	if (newNode == NULL)
		return NULL;
	newNode->data = data;
	newNode->Lchild = NULL;
	newNode->Rchild = NULL;
	return newNode;
}
//连接结点
void insertNode(LPTREE parent, LPTREE Lchild, LPTREE Rchild)
{
	parent->Lchild = Lchild;
	parent->Rchild = Rchild;
}
int main()
{
	//第一种创建方式
	LPTREE A = createNode('A');
	LPTREE B = createNode('B');
	LPTREE C = createNode('C');
	LPTREE D = createNode('D');
	LPTREE E = createNode('E');
	LPTREE F = createNode('F');
	LPTREE G = createNode('G');
	insertNode(A, B, C);
	insertNode(B, D, NULL);
	insertNode(D, NULL, G);
	insertNode(C, E, F);
    return 0;
}

递归法创建法

#include<stdio.h>
#include<stdlib.h>
//1.结构体描述
typedef struct Node
{
	char data;               //数据
	struct Node* Lchild;     //左子树指针
	struct Node* Rchild;     //右子树指针
}TREE,*LPTREE;
/3.递归创建法
void createTree(LPTREE* T)
{
	char userKey = '\0';
	scanf_s("%c", &userKey, 1);
	if (userKey == '#')
	{
		*T = NULL;
	}
	else
	{
		*T = (LPTREE)malloc(sizeof(TREE));
		if (*T == NULL)
		{
			return;
		}
		(*T)->data = userKey;
		createTree(&(*T)->Lchild);
		createTree(&(*T)->Rchild);
	}
}
int main()
{
    LPTREE A = NULL;
    createTree(&A);
    return 0;
}

先序遍历:根----->左----->右

  • 先序遍历的概念:

①若二叉树为空,则空操作返回;

(否则)

②访问根结点;

③先序遍历根结点的左子树;

④先序遍历根结点的右子树。

//用递归实现先序遍历
void preOrder(LPTREE root)
{
	//根左右
	if (root != NULL)
	{
		printf("%c\t", root->data);
		preOrder(root->Lchild);
		preOrder(root->Rchild);
	}
}
//非递归遍历:先序遍历(用栈实现)
void preOrderByStack(LPTREE root)
{
	if (root == NULL)
	{
		return;
	}
	LPTREE pMove = root;
	//准备一个栈
	LPTREE stack[1000];
	int stackTop = -1;
	while (stackTop != -1 || pMove != NULL)
	{
		//往左边走,边走边打印,边入栈
		while (pMove != NULL)
		{
			printf("%c\t", pMove->data);
			stack[++stackTop] = pMove;
			pMove = pMove->Lchild;
		}
		if (stack != -1)
		{
			pMove = stack[stackTop--];
			pMove = pMove->Rchild;
		}
	}
}

中序遍历:左----->根----->右

  • 中序遍历的概念:

①若二叉树为空,则空操作返回;

(否则)

②中序遍历根结点的左子树;

③访问根结点;

④中序遍历根结点的右子树。

//用递归实现中序遍历
void midOrder(LPTREE root)
{
	if (root != NULL)
	{
		//左根右 
		midOrder(root->Lchild);
		printf("%c\t", root->data);
		midOrder(root->Rchild);
	}
}
//非递归遍历:中序遍历(用栈实现)
void midOrderByStack(LPTREE root)
{
	if (root == NULL)
	{
		return;
	}
	LPTREE pMove = root;
	//准备一个栈
	LPTREE stack[1000];
	int stackTop = -1;
	while (stackTop != -1 || pMove != NULL)
	{
		while (pMove != NULL)
		{
			stack[++stackTop] = pMove;
			pMove = pMove->Lchild;
		}
		if (stackTop != -1)
		{
			pMove = stack[stackTop--];
			printf("%c\t", pMove->data);
			pMove = pMove->Rchild;
		}
	}
}

后序遍历:左----->右----->根

  • 后序遍历的概念:

①若二叉树为空,则空操作返回;

(否则)

②后序遍历根结点的左子树;

③后序遍历根结点的右子树。

④访问根结点;

//用递归实现后序遍历
void lastOrder(LPTREE root)
{
	if (root != NULL)
	{
		//左右根
		lastOrder(root->Lchild);
		lastOrder(root->Rchild);
		printf("%c\t", root->data);
	}
}
//非递归遍历:后序遍历(用栈实现)
void lastOrderByStack(LPTREE root)
{
	if (root == NULL)
		return;
	LPTREE stack[1000];
	int stackTop = -1;
	LPTREE pMove = root;
	LPTREE plastvisit = NULL;
	while (pMove)
	{
		stack[++stackTop] = pMove;
		pMove = pMove->Lchild;
	}
	while (stackTop != -1)
	{
		pMove = stack[stackTop--];	//D
		if (pMove->Rchild == NULL || pMove->Rchild == plastvisit)
		{
			printf("%c\t", pMove->data);
			plastvisit = pMove;
		}
		else
		{
			stack[++stackTop] = pMove;
			pMove = pMove->Rchild;
			while (pMove)
			{
				stack[++stackTop] = pMove;
				pMove = pMove->Lchild;
			}
		}
	}
}

层序遍历:队列的使用

  • 层次遍历的概念二叉树的层次遍历是指从二叉树的第一层(即根结点)开始,从上至下逐层遍历,在同一层中,则按从左到右的顺序对结点逐个访问。
//层次遍历:队列的使用
void layerOder(LPTREE root)
{
	//准备一个移动结点
	LPTREE pMove = root;
	//准备一个队列
	LPTREE queue[1000];
	int front = 0;
	int tail = 0;
	queue[tail++] = pMove;
	printf("%c\t", pMove->data);
	while (front != tail)
	{
		//出队
		pMove = queue[front++];
		if (pMove->Lchild != NULL)
		{
			queue[tail++] = pMove->Lchild;
			printf("%c\t", pMove->Lchild->data);
		}
		if (pMove->Rchild != NULL)
		{
			queue[tail++] = pMove->Rchild;
			printf("%c\t", pMove->Rchild->data);
		}
	}
}

综合代码

#include<stdio.h>
#include<stdlib.h>
//1.结构体描述
typedef struct Node
{
	char data;               //数据
	struct Node* Lchild;     //左子树指针
	struct Node* Rchild;     //右子树指针
}TREE,*LPTREE;
//2.呆比法创建法
//把数据变成一个结点
LPTREE createNode(char data)
{
	LPTREE newNode = (LPTREE)malloc(sizeof(TREE));
	if (newNode == NULL)
		return NULL;
	newNode->data = data;
	newNode->Lchild = NULL;
	newNode->Rchild = NULL;
	return newNode;
}
//连接结点
void insertNode(LPTREE parent, LPTREE Lchild, LPTREE Rchild)
{
	parent->Lchild = Lchild;
	parent->Rchild = Rchild;
}
//3.递归创建法
void createTree(LPTREE* T)
{
	char userKey = '\0';
	scanf_s("%c", &userKey, 1);
	if (userKey == '#')
	{
		*T = NULL;
	}
	else
	{
		*T = (LPTREE)malloc(sizeof(TREE));
		if (*T == NULL)
		{
			return;
		}
		(*T)->data = userKey;
		createTree(&(*T)->Lchild);
		createTree(&(*T)->Rchild);
	}
}
//用递归实现先序遍历
void preOrder(LPTREE root)
{
	//根左右
	if (root != NULL)
	{
		printf("%c\t", root->data);
		preOrder(root->Lchild);
		preOrder(root->Rchild);
	}
}
//非递归遍历:先序遍历(用栈实现)
void preOrderByStack(LPTREE root)
{
	if (root == NULL)
	{
		return;
	}
	LPTREE pMove = root;
	//准备一个栈
	LPTREE stack[1000];
	int stackTop = -1;
	while (stackTop != -1 || pMove != NULL)
	{
		//往左边走,边走边打印,边入栈
		while (pMove != NULL)
		{
			printf("%c\t", pMove->data);
			stack[++stackTop] = pMove;
			pMove = pMove->Lchild;
		}
		if (stack != -1)
		{
			pMove = stack[stackTop--];
			pMove = pMove->Rchild;
		}
	}
}
//用递归实现中序遍历
void midOrder(LPTREE root)
{
	if (root != NULL)
	{
		//左根右 
		midOrder(root->Lchild);
		printf("%c\t", root->data);
		midOrder(root->Rchild);
	}
}
//非递归遍历:中序遍历(用栈实现)
void midOrderByStack(LPTREE root)
{
	if (root == NULL)
	{
		return;
	}
	LPTREE pMove = root;
	//准备一个栈
	LPTREE stack[1000];
	int stackTop = -1;
	while (stackTop != -1 || pMove != NULL)
	{
		while (pMove != NULL)
		{
			stack[++stackTop] = pMove;
			pMove = pMove->Lchild;
		}
		if (stackTop != -1)
		{
			pMove = stack[stackTop--];
			printf("%c\t", pMove->data);
			pMove = pMove->Rchild;
		}
	}
}
//用递归实现后序遍历
void lastOrder(LPTREE root)
{
	if (root != NULL)
	{
		//左右根
		lastOrder(root->Lchild);
		lastOrder(root->Rchild);
		printf("%c\t", root->data);
	}
}
//非递归遍历:后序遍历(用栈实现)
void lastOrderByStack(LPTREE root)
{
	if (root == NULL)
		return;
	LPTREE stack[1000];
	int stackTop = -1;
	LPTREE pMove = root;
	LPTREE plastvisit = NULL;
	while (pMove)
	{
		stack[++stackTop] = pMove;
		pMove = pMove->Lchild;
	}
	while (stackTop != -1)
	{
		pMove = stack[stackTop--];	//D
		if (pMove->Rchild == NULL || pMove->Rchild == plastvisit)
		{
			printf("%c\t", pMove->data);
			plastvisit = pMove;
		}
		else
		{
			stack[++stackTop] = pMove;
			pMove = pMove->Rchild;
			while (pMove)
			{
				stack[++stackTop] = pMove;
				pMove = pMove->Lchild;
			}
		}
	}
}
//层次遍历:队列的使用
void layerOder(LPTREE root)
{
	//准备一个移动结点
	LPTREE pMove = root;
	//准备一个队列
	LPTREE queue[1000];
	int front = 0;
	int tail = 0;
	queue[tail++] = pMove;
	printf("%c\t", pMove->data);
	while (front != tail)
	{
		//出队
		pMove = queue[front++];
		if (pMove->Lchild != NULL)
		{
			queue[tail++] = pMove->Lchild;
			printf("%c\t", pMove->Lchild->data);
		}
		if (pMove->Rchild != NULL)
		{
			queue[tail++] = pMove->Rchild;
			printf("%c\t", pMove->Rchild->data);
		}
	}
}
int main()
{
	//第一种创建方式
	LPTREE A = createNode('A');
	LPTREE B = createNode('B');
	LPTREE C = createNode('C');
	LPTREE D = createNode('D');
	LPTREE E = createNode('E');
	LPTREE F = createNode('F');
	LPTREE G = createNode('G');
	insertNode(A, B, C);
	insertNode(B, D, NULL);
	insertNode(D, NULL, G);
	insertNode(C, E, F);
	//第二种创建方式
	//LPTREE A = NULL;
	//createTree(&A);
	preOrder(A);
	printf("\n");
	//layerOrder(A);
	preOrderByStack(A);
	printf("\n");
	midOrder(A);
	printf("\n");
	midOrderByStack(A);
	printf("\n");
	lastOrderByStack(A);
	printf("\n");
	lastOrder(A);
	printf("\n");
	return 0;
}

11.二叉搜索树BST

  • 用二叉搜索树来存储有序集,每一个结点存储一个元素

  • 满足:存储每个结点的元素x大于其左子树中任一结点中所存储的元素,

    小于其右子树中任一结点中所存储的元素

  • 任何一颗二叉搜索树的子树都是一颗二叉搜索树

  • 二叉搜索树用中序遍历的话,结果是有序的

  • 查找效率快

二叉树搜索的定义

typedef struct dataPair
{
	int first;            //比较准备
	char second[20];      //字符串数据位例
}DATA,*LPDATA;

//二叉树的结点
typedef struct treeNode
{
	DATA data;
	struct treeNode* LChild;
	struct treeNode* RChild;
}NODE,*LPNODE;

//创建结点
LPNODE createNode(DATA data)
{
	LPNODE newNode = (LPNODE)malloc(sizeof(NODE));
	newNode->data = data;
	newNode->LChild = NULL;
	newNode->RChild = NULL;
	return newNode;
}

//二叉搜索树 BST bianry search Tree
typedef struct bianrySearchTree
{
	LPNODE root;     //根结点
	int treeSize;
}BST,*LPBST;

//创建树就是描述树的最初状态,从无到有
LPBST createBST()
{
	LPBST tree = (LPBST)malloc(sizeof(BST));
	tree->root = NULL;
	tree->treeSize = 0;
	return tree;
}
//万金油函数
int size(LPBST tree)
{
	return tree->treeSize;
}
int empty(LPBST tree)
{
	return tree->treeSize == 0;
}

二叉搜索树的插入

//插入结点(二叉搜索树)
void insertNode(LPBST tree, DATA data)
{
	LPNODE newNode = createNode(data);
	//找到合适的位置
	LPNODE pMove = tree->root;
	LPNODE pMoveParent = NULL;
	while (pMove != NULL)
	{
		pMoveParent = pMove;
		//怎么走:
		if (data.first < pMove->data.first)
		{
			pMove = pMove->LChild;
		}
		else if (data.first > pMove->data.first)
		{
			pMove = pMove->RChild;
		}
		else
		{
            //相同的就覆盖
			strcpy(pMove->data.second, data.second);
			return;
		}
	}
	//分析查找结果
	if (tree->root == NULL)
	{
		tree->root = newNode;
	}
	else
	{
		//如何不为NULL,考虑插在parent左边还是右边
		if (pMoveParent->data.first > data.first)
		{
			pMoveParent->LChild = newNode;
		}
		else
		{
			pMoveParent->RChild = newNode;
		}
	}
	tree->treeSize++;
}

二叉搜索树的查找

//查找结点元素
LPNODE searchBST(LPBST tree, int first)
{
	LPNODE pMove = tree->root;
	while (pMove != NULL && pMove->data.first != first)
	{
		if (pMove->data.first > first)
		{
			pMove = pMove->LChild;
		}
		else
		{
			pMove = pMove->RChild;
		}
	}
	return pMove;
}

中序遍历(有序)

//二叉树的中序遍历,结果是有序的 
void midOrder(LPNODE tree)
{
	if (tree != NULL)
	{
		midOrder(tree->LChild);
		printNode(tree);
		midOrder(tree->RChild);
	}
}

二叉搜索树的删除(难点)

​ 思路:

    1. 如果删除结点在叶结点位置,则直接删除
    1. 如果删除结点只有一个子结点,则将父节点的指针指向要删除的结点的孩子结点
    1. 如果删除的结点有左右两个子树,则用另一结点代替删除结点

调整结点:

    1. 从删除结点的左子树中找最右边的结点放上去
    1. 从删除结点的右子树中找最左边的结点放上去
//二叉搜索树的删除
void deleteNode(LPBST tree, int first)
{
	//1.查找部分
	LPNODE pMove = tree->root;
	LPNODE pMoveParent = NULL;
	while (pMove != NULL && pMove->data.first != first)
	{
		pMoveParent = pMove;
		if (first < pMove->data.first)
		{
			pMove = pMove->LChild;
		}
		else
		{
			pMove = pMove->RChild;
		}
	}
	//分析结果
	if (pMove == NULL)
	{
		printf("无此处,删除失败\n");
		return;
	}
	//左右子树都存在的情况下
	if (pMove->LChild != NULL && pMove->RChild != NULL)
	{
		//找到的结点的左边结点一直往右找到最后一个结点补充删除的结点 
		LPNODE moveNode = pMove->LChild;
		LPNODE moveNodeParent = pMove;
		while (moveNode->RChild != NULL)
		{
			moveNodeParent = moveNode;
			moveNode = moveNode->RChild;
		}
		//创建一个新的结点
		LPNODE newNode = createNode(moveNode->data);
		newNode->LChild = pMove->LChild;
		newNode->RChild = pMove->RChild;
		//如果要删除的是根结点
		if (pMoveParent == NULL)
		{
			tree->root = newNode;
		}
		//如果删除的结点是左边
		else if (pMove == pMoveParent->LChild)
		{
			pMoveParent->LChild = newNode;
		}
		//如果删除的结点是右边
		else
		{
			pMoveParent->RChild = newNode;
		}
		//调整二叉树
		//改变删除指针的位置
		if (moveNodeParent == pMove)       //调整的结点没有右子树
		{
			pMoveParent = newNode;     
		}              
		else                              //调整的结点有右子树
		{
			pMoveParent = moveNodeParent;
		}
		free(pMove);
		pMove = moveNode;               //删除指针调整到要调整节点位置
	}
	LPNODE sNode = NULL;
	if (pMove->LChild != NULL)
		sNode = pMove->LChild;
	else
		sNode = pMove->RChild;
	if (tree->root == pMove)
	{
		tree->root = sNode;
	}
	else
	{
		if (pMove == pMoveParent->LChild)
		{
			pMoveParent->LChild = sNode;
		}
		else
		{
			pMoveParent->RChild = sNode;
		}
	}
	free(pMove);
	tree->treeSize--;
}

综合代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//处理二叉搜索树的数据
typedef struct dataPair
{
	int first;            //比较准备
	char second[20];      //字符串数据位例
}DATA,*LPDATA;

//二叉树的结点
typedef struct treeNode
{
	DATA data;
	struct treeNode* LChild;
	struct treeNode* RChild;
}NODE,*LPNODE;

//创建结点
LPNODE createNode(DATA data)
{
	LPNODE newNode = (LPNODE)malloc(sizeof(NODE));
	newNode->data = data;
	newNode->LChild = NULL;
	newNode->RChild = NULL;
	return newNode;
}

//二叉搜索树 BST bianry search Tree
typedef struct bianrySearchTree
{
	LPNODE root;     //根结点
	int treeSize;
}BST,*LPBST;

//创建树就是描述树的最初状态,从无到有
LPBST createBST()
{
	LPBST tree = (LPBST)malloc(sizeof(BST));
	tree->root = NULL;
	tree->treeSize = 0;
	return tree;
}
//万金油函数
int size(LPBST tree)
{
	return tree->treeSize;
}
int empty(LPBST tree)
{
	return tree->treeSize == 0;
}

//插入结点(二叉搜索树)
void insertNode(LPBST tree, DATA data)
{
	LPNODE newNode = createNode(data);
	//找到合适的位置
	LPNODE pMove = tree->root;
	LPNODE pMoveParent = NULL;
	while (pMove != NULL)
	{
		pMoveParent = pMove;
		//怎么走:
		if (data.first < pMove->data.first)
		{
			pMove = pMove->LChild;
		}
		else if (data.first > pMove->data.first)
		{
			pMove = pMove->RChild;
		}
		else
		{
			strcpy(pMove->data.second, data.second);
			return;
		}
	}
	//分析查找结果
	if (tree->root == NULL)
	{
		tree->root = newNode;
	}
	else
	{
		//如何不为NULL,考虑插在parent左边还是右边
		if (pMoveParent->data.first > data.first)
		{
			pMoveParent->LChild = newNode;
		}
		else
		{
			pMoveParent->RChild = newNode;
		}
	}
	tree->treeSize++;
}
//查找结点元素
LPNODE searchBST(LPBST tree, int first)
{
	LPNODE pMove = tree->root;
	while (pMove != NULL && pMove->data.first != first)
	{
		if (pMove->data.first > first)
		{
			pMove = pMove->LChild;
		}
		else
		{
			pMove = pMove->RChild;
		}
	}
	return pMove;
}

//输出树结点
void printNode(LPNODE curNode)
{
	printf("%d\t%s\n", curNode->data.first, curNode->data.second);
}

//二叉树的中序遍历,结果是有序的 
void midOrder(LPNODE tree)
{
	if (tree != NULL)
	{
		midOrder(tree->LChild);
		printNode(tree);
		midOrder(tree->RChild);
	}
}
//二叉搜索树的删除
void deleteNode(LPBST tree, int first)
{
	//1.查找部分
	LPNODE pMove = tree->root;
	LPNODE pMoveParent = NULL;
	while (pMove != NULL && pMove->data.first != first)
	{
		pMoveParent = pMove;
		if (first < pMove->data.first)
		{
			pMove = pMove->LChild;
		}
		else
		{
			pMove = pMove->RChild;
		}
	}
	//分析结果
	if (pMove == NULL)
	{
		printf("无此处,删除失败\n");
		return;
	}
	//左右子树都存在的情况下
	if (pMove->LChild != NULL && pMove->RChild != NULL)
	{
		//找到的结点的左边结点一直往右找到最后一个结点补充删除的结点 
		LPNODE moveNode = pMove->LChild;
		LPNODE moveNodeParent = pMove;
		while (moveNode->RChild != NULL)
		{
			moveNodeParent = moveNode;
			moveNode = moveNode->RChild;
		}
		//创建一个新的结点
		LPNODE newNode = createNode(moveNode->data);
		newNode->LChild = pMove->LChild;
		newNode->RChild = pMove->RChild;
		//如果要删除的是根结点
		if (pMoveParent == NULL)
		{
			tree->root = newNode;
		}
		//如果删除的结点是左边
		else if (pMove == pMoveParent->LChild)
		{
			pMoveParent->LChild = newNode;
		}
		//如果删除的结点是右边
		else
		{
			pMoveParent->RChild = newNode;
		}
		//调整二叉树
		//改变删除指针的位置
		if (moveNodeParent == pMove)       //调整的结点没有右子树
		{
			pMoveParent = newNode;     
		}              
		else                              //调整的结点有右子树
		{
			pMoveParent = moveNodeParent;
		}
		free(pMove);
		pMove = moveNode;               //删除指针调整到要调整节点位置
	}
	LPNODE sNode = NULL;
	if (pMove->LChild != NULL)
		sNode = pMove->LChild;
	else
		sNode = pMove->RChild;
	if (tree->root == pMove)
	{
		tree->root = sNode;
	}
	else
	{
		if (pMove == pMoveParent->LChild)
		{
			pMoveParent->LChild = sNode;
		}
		else
		{
			pMoveParent->RChild = sNode;
		}
	}
	free(pMove);
	tree->treeSize--;
}
int main()
{
	LPBST tree = createBST();
	DATA data[8] = { 10,"无敌",18,"大力",3,"菜鸟",8,"Bug",12,"LZG",2,"ZJJ",7,"一笙",4,"风云龙" };
	for (int i = 0; i < 8; i++)
	{
		insertNode(tree, data[i]);
	}
	midOrder(tree->root);
	LPNODE result = searchBST(tree, 100);
	if (result == NULL)
	{
		printf("\n未找到相关信息!\n");
	}
	printf("18:%s\n", searchBST(tree, 18)->data.second);
	printf("\n删除3节点:\n");
	deleteNode(tree, 3);
	midOrder(tree->root);
	return 0;
}

12.平衡二叉树AVL

AVL 基础

  • AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的。

  • 它是最先发明的自平衡二叉查找树,也被称为高度平衡树。相比于"二叉查找树",它的特点是:AVL树中任何节点的两个子树的高度最大差别为1。

  • AVL树的查找、插入和删除在平均和最坏情况下都是O(logn)。

  • 如果在AVL树中插入或删除节点后,使得高度之差大于1。此时,AVL树的平衡状态就被破坏,它就不再是一棵二叉树;为了让它重新维持在一个平衡状态,就需要对其进行旋转处理。学AVL树,重点的地方也就是它的旋转算法

旋转:

  • 如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右)和RL(右左)。

13.哈夫曼树(最优树)

14.图的基础

图定义

  • 图G由两个集合V和E组成,记为 G = ( V , E )

  • V是顶点的有穷非空集合,E是V中顶点偶对的有穷集,这些顶点偶对称为边。通常V(G)和E(G)分别称为图的顶点集合和边集合

  • E(G)可以为空集。

  • 图的数据结构的形式化定义

  • G = ( V , E )

    • 其中 V = { x | x dataobject }
    • E ={VR}
    • VR={<x,y>| p(x,y)  ( x , y  V ) }

    VR是两顶点间的关系的集合,即边的集合。

有向图

有向图:对一个图G,若边集E(G)为有向边的集合,则称该图为有向图

  • G1=(V,E)
  • V={v1, v2, v3, v4}
  • E={<v1, v2>, <v1, v3>, <v3, v4>, <v4, v1>}
  • 其中<x, y>表示从x到y的一条弧(arc),A为弧集合,x为弧尾(tail),y为弧头(head)

无向图

无向图:对一个图G,若边集E(G)为无向边的集合,则称该图为无向图。

  • G2=(V,E)

  • V={v1, v2, v3, v4, v5}

  • E={(v1, v2), (v1, v3), (v2, v3), (v3, v4), (v2,v5), (v3,v5)}

  • 其中,(x, y)表示x与y之间的一条连线,称为边(edge)

边和顶点关系

  • 设n为顶点数,e为边或弧的条数

    • 对无向图有:0 ≤ e ≤ n(n-1)/2
    • 有向图有:0≤ e ≤ n(n-1)

    证明:对有向图,每个顶点至多有n-1条边与其它的n-1个顶点相连,则n个顶点至多有n(n-1)条边。但对无向图,每条边连接2个顶点,故最多为n(n-1)/2

其他:自行百度

15.图的存储方式和遍历方式

  • 邻接矩阵(这里用)

    • 一维数组存储顶点

    • 二维数组描述边

  • 邻接表

    • 链表来描述边

邻接矩阵存储图

  • 给定二维数组初始化
//图结构
struct Graph
{
	int vertexs;    //顶点数量
	int edges;      //边数
	char* pVertex;      //一维数组描述顶点
	int** pEdge;        //二维数组描述边
};
//图的初始化
void initGrap(struct Graph* g, int vertexs, int edges, char* v, int map[VERTEX][VERTEX])
{
	g->vertexs = vertexs;
	g->edges = edges;
	//1.开内存
	g->pVertex = (char*)malloc(sizeof(char)*g->vertexs);
	g->pEdge = (int**)malloc(sizeof(int*)*g->vertexs);
	for (int i = 0; i < g->vertexs; i++)
	{
		g->pEdge[i] = (int*)malloc(sizeof(int)*g->vertexs);
	}
	//2.数据赋值
#if 0
	for (int i = 0; i < g->vertexs; i++)
	{
		g->pVertex[i] = v[i];
	}
#else
	memcpy(g->pVertex, v, sizeof(char) * g->vertexs);
#endif
	for (int i = 0; i < g->vertexs; i++)
	{
		for (int j = 0; j < g->vertexs; j++)
		{
			g->pEdge[i][j] = map[i][j];
		}
	}
}
  • 用户输入的方式初始图
//根据顶点找对应下标
int getIdx(struct Graph* g,char c)
{
	for (int i = 0; i < g->vertexs; i++)
	{
		if (c == g->pVertex[i]) return i;
	}
	return -1;
}
//用户输入的方式初始图
void createGraph(struct Graph* g)
{
	printf("请输入顶点个数:");
	scanf("%d", &(g->vertexs));
	printf("请输入边的条数:");
	scanf("%d", &(g->edges));
	g->pVertex = (char*)malloc(sizeof(char) * g->vertexs);
	g->pEdge = (int**)malloc(sizeof(int*) * g->vertexs);
	for (int i = 0; i < g->vertexs; i++)
	{
		g->pEdge[i] = (int*)malloc(sizeof(int) * g->vertexs);
		memcpy(g->pEdge[i], 0, g->vertexs * sizeof(int));
	}
	printf("请输入所有的顶点:");
	scanf("%s",g->pVertex);

	//A->B
	char buff[5];    //buff[0]是起始的顶点    buff[3]是目的顶点
	int begIdx, endIdx;
	for (int i = 0; i < g->edges; i++)
	{
		printf("请输入第%d条边:(A->B)", i + 1);
		scanf("%s", buff);
		begIdx = getIdx(g, buff[0]);
		endIdx = getIdx(g, buff[3]);

		//有向图
		//g->pEdge[begIdx][endIdx] = 1;
		//无向图
		g->pEdge[begIdx][endIdx] = 1;
		g->pEdge[endIdx][begIdx] = 1;
	}
}

显示图结构

  • 就是把邻接矩阵打印出来
//显示图结构
void showGraph(struct Graph* g)
{
	for (int i = 0; i <= g->vertexs; i++)
	{
		for (int j = 0; j <= g->vertexs; j++)
		{
			if (i == 0 && j == 0)
			{
				printf("  ");
			}
			else if (i == 0)
			{
				printf("%c ", g->pVertex[j-1]);
			}
			else if (j == 0)
			{
				printf("%c ", g->pVertex[i-1]);
			}
			else
			{
				printf("%d ", g->pEdge[i - 1][j - 1]);
			}
		}
		printf("\n");
	}
}

深度优先遍历DFS

  • 先输出起点,标记起点走过
    • 然后循环找下一个
      • 找到了,就输出,并标记走过
      • 没找到
        • 全部都输出过了
          结束
          还有没输出的
          就找到顶点数组中第一个没输出的点,继续循环
//根据顶点找对应下标
int getIdx(struct Graph* g,char c)
{
	for (int i = 0; i < g->vertexs; i++)
	{
		if (c == g->pVertex[i]) return i;
	}
	return -1;
}
//找下一个相邻顶点(没有找过的),找到了返回下标,没找到返回-1
int findFirstVertex(struct Graph* g, int begIdx, bool isFind[VERTEX])
{
	for (int i = 0; i < g->vertexs; i++)
	{
		if (isFind[i]) continue;
		if (g->pEdge[begIdx][i] == 1) return i;
	}
	return -1;
}
void DFS(struct Graph* g, const char beg, bool isFind[VERTEX])
{
	int currentIdx = getIdx(g, beg);
	isFind[currentIdx] = 1;          //标记已经走过了
	int nextIdx = findFirstVertex(g, currentIdx, isFind);   //找下一个
	while (true)
	{
		if (nextIdx == -1)      //走完了(没找到)
		{
			break;
		}
		else                  //找到了
		{
			if (isFind[nextIdx] == 0)
			{
				printf("%c ", g->pVertex[nextIdx]);
				DFS(g, g->pVertex[nextIdx], isFind);
			}
			//找下一个
			nextIdx = findFirstVertex(g, currentIdx, isFind);
		}
	}
}

广度优先遍历BFS

//根据顶点找对应下标
int getIdx(struct Graph* g,char c)
{
	for (int i = 0; i < g->vertexs; i++)
	{
		if (c == g->pVertex[i]) return i;
	}
	return -1;
}
//找下一个相邻顶点(没有找过的),找到了返回下标,没找到返回-1
int findFirstVertex(struct Graph* g, int begIdx, bool isFind[VERTEX])
{
	for (int i = 0; i < g->vertexs; i++)
	{
		if (isFind[i]) continue;
		if (g->pEdge[begIdx][i] == 1) return i;
	}
	return -1;
}
void BFS(struct Graph* g, const char beg, bool isFind[VERTEX])
{
	std::queue<int> q;
	int current = getIdx(g, beg);
	q.push(current);              //把第一个放在队列
	isFind[current] = 1;
	int headIdx;	
	int idx;
	while (!q.empty())
	{
		headIdx = q.front();  //队列头,第一个
		q.pop();              //删掉第一个
		//把相邻的顶点放在队列中
		idx = findFirstVertex(g, headIdx, isFind);
		while (true)
		{
			if (idx == -1)
			{
				break;
			}
			if (isFind[idx] == 0)    //没走过
			{
				isFind[idx] = 1;
				printf("%c ", g->pVertex[idx]);
				q.push(idx);
			}
			idx = findFirstVertex(g, headIdx, isFind);
		}
	}
}

16.A*算法

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值