目录
第一章 线性表
顺序表 Sequence Table
带头双向循环链表 Linked List
第二章 栈和队列
栈 Stack (顺序存储) 检查括号是否匹配
队列 Queue (链式存储)
循环队列 Circle Queue
第三章 串
模拟实现string.h库函数
第四章 广义表
广义表的实现 利用递归思想
第五章 树
二叉树的构建与遍历 递归思想
第六章 图
图的常用术语
图的存储结构
第七章 查找
二叉排序树的构建:
第八章 内排序
直接插入排序
希尔排序
直接选择排序
堆排序
冒泡排序
快速排序
归并排序
计数排序
基数排序
第一章 线性表
- 一个线性表是n个数据元素的优先序列
 - 线性表可分为顺序存储结构(数组)和链式存储结构(链表)
 - 链表可分为单链表、循环链表、双向链表
 
顺序表 Sequence Table
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#define CAPACITY 4
typedef int SData;
typedef struct Sequence
{
	SData* data;
	int size;
	int capacity;
}Sequence;
void Init(Sequence* ps)
{
	assert(ps);
	ps->data = (SData*)malloc(sizeof(SData) * CAPACITY);
	ps->size = 0;
	ps->capacity = CAPACITY;
}
void CheckCapacity(Sequence* ps)
{
	if (ps->size == ps->capacity)
	{
		ps->data = realloc(ps->data, sizeof(SData) * ps->size * 2);
		if (ps->data == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->capacity *= 2;
	}
}
bool Empty(Sequence* ps)
{
	assert(ps);
	if (ps->size == 0)
		return true;
	return false;
}
void Push(Sequence* ps, SData x)
{
	assert(ps);
	CheckCapacity(ps);
	ps->data[ps->size] = x;
	++ps->size;
}
void Pop(Sequence* ps)
{
	assert(ps);
	assert(!Empty(ps));
	--ps->size;
}
int Find(Sequence* ps, SData x)
{
	assert(ps);
	int pos = 0;
	for (; pos < ps->size; ++pos)
	{
		if (ps->data[pos] == x)
			return pos;
	}
	return -1;
}
void Insert(Sequence* ps, int pos, SData x)
{
	assert(ps);
	assert(pos <= ps->size);
	CheckCapacity(ps);
	int i = ps->size;
	for (; i >= pos; --i)
	{
		ps->data[i + 1] = ps->data[i];
	}
	ps->data[pos] = x;
	++ps->size;
}
void Erase(Sequence* ps, int pos)
{
	assert(ps);
	assert(!Empty(ps));
	int i = pos;
	for (; i < ps->size - 1; ++i)
	{
		ps->data[i] = ps->data[i + 1];
	}
	--ps->size;
}
void Print(Sequence* ps)
{
	assert(ps);
	int i = 0;
	for (; i < ps->size; ++i)
	{
		printf("%d ", ps->data[i]);
	}
	printf("\n");
}
int main()
{
	Sequence s;
	Init(&s);
	Push(&s, 1);
	Push(&s, 2);
	Push(&s, 3);
	Push(&s, 4);
	Push(&s, 5);
	Print(&s);
	Insert(&s, 0, 10);
	Insert(&s, 3, 20);
	Insert(&s, 7, 30);
	Print(&s);
	Pop(&s);
	Erase(&s, 0);
	Erase(&s, Find(&s, 20));
	Print(&s);
	return 0;
}
 
带头双向循环链表 Linked List
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int LData;
typedef struct List
{
	LData data;
	struct List* next;
	struct List* prev;
}List;
void Init(List* pl)
{
	assert(pl);
	pl->next = pl;
	pl->prev = pl;
}
bool Empty(List* pl)
{
	assert(pl);
	if (pl->next == pl)
		return true;
	return false;
}
void Insert(List* pl, List* pos, LData x)
{
	assert(pl);
	List* new_node = (List*)malloc(sizeof(List));
	new_node->data = x;
	new_node->next = pos;
	new_node->prev = pos->prev;
	pos->prev->next = new_node;
	pos->prev = new_node;
}
void Erase(List* pl, List* pos)
{
	assert(pl);
	assert(pos);
	assert(!Empty(pl));
	List* next = pos->next;
	List* prev = pos->prev;
	prev->next = next;
	next->prev = prev;
	free(pos);
	pos = NULL;
}
void Push(List* pl, LData x)
{
	assert(pl);
	if (Empty(pl))
	{
		List* new_node = (List*)malloc(sizeof(List));
		new_node->data = x;
		new_node->prev = pl;
		new_node->next = pl;
		pl->next = new_node;
		pl->prev = new_node;
	}
	else
	{
		Insert(pl, pl, x);
	}
}
void Pop(List* pl)
{
	assert(pl);
	assert(!Empty(pl));
	Erase(pl, pl->prev);
}
List* Find(List* pl, LData x)
{
	assert(pl);
	List* cur = pl->next;
	while (cur != pl)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}
void Print(List* pl)
{
	assert(pl);
	List* cur = pl->next;
	while (cur != pl)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
int main()
{
	List list;
	Init(&list);
	Push(&list, 1);
	Push(&list, 2);
	Push(&list, 3);
	Push(&list, 4);
	Print(&list);
	Insert(&list, Find(&list, 1), 10);
	Insert(&list, Find(&list, 2), 20);
	Insert(&list, Find(&list, 4), 40);
	Print(&list);
	Pop(&list);
	Erase(&list, Find(&list, 10));
	Erase(&list, Find(&list, 20));
	Print(&list);
	Print(&list);
	return 0;
} 
 
 
第二章 栈和队列
- 栈:先进后出,一般为顺序存储结构
 - 队列:先进先出,一般为链式存储结构
 
循环队列:
- 顺序存储结构,设置容量(capacity)及头尾下标(front、rear)
 - push:++rear, pop:++front,当下标等于capacity时变为0
 - 为区分“满”和“空”,舍弃一个元素空间,使 (rear + 1) % capacity == front 为判“满”
 
栈 Stack (顺序存储) 检查括号是否匹配
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#define CAPACITY 4
typedef char SData;
typedef struct Stack
{
	SData* data;
	int size;
	int capacity;
}Stack;
void Init(Stack* s)
{
	assert(s);
	s->data = (SData*)malloc(sizeof(SData) * CAPACITY);
	s->size = 0;
	s->capacity = CAPACITY;
}
bool Empty(Stack* s)
{
	assert(s);
	if (s->size == 0)
		return true;
	return false;
}
void CheckCapacity(Stack* s)
{
	assert(s);
	if (s->size == s->capacity)
	{
		s->data = (SData*)realloc(s->data, sizeof(SData) * s->capacity * 2);
		s->capacity *= 2;
	}
}
void Push(Stack* s, SData x)
{
	assert(s);
	CheckCapacity(s);
	s->data[s->size] = x;
	++s->size;
}
void Pop(Stack* s)
{
	assert(s);
	assert(!Empty(s));
	--s->size;
}
SData Top(Stack* s)
{
	assert(s);
	return s->data[s->size - 1];
}
int Size(Stack* s)
{
	assert(s);
	return s->size;
}
void CheckBrackets(Stack* s, char* str)
{
	assert(s);
	char* cur = str;
	while (*cur != '\0')
	{
		if (*cur == '{' || *cur == '[' || *cur == '(')
		{
			Push(s, *cur);
		}
		else if (*cur == '}' || *cur == ']' || *cur == ')')
		{
			if (*cur == '}' && Top(s) == '{')
			{
				Pop(s);
			}
			else if (*cur == ']' && Top(s) == '[')
			{
				Pop(s);
			}
			else if (*cur == ')' && Top(s) == '(')
			{
				Pop(s);
			}
			else
			{
				printf("括号不匹配\n");
				return;
			}
		}
		++cur;
	}
	if (Empty(s))
		printf("括号匹配\n");
	else
		printf("括号不匹配\n");
}
int main()
{
	Stack s;
	Init(&s);
	char str[20] = "{[(()*)adf]}";
	CheckBrackets(&s, str);
	return 0;
} 
 
队列 Queue (链式存储)
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int LData;
typedef struct Node
{
	LData data;
	struct Node* next;
}Node;
typedef struct Queue
{
	Node* head;
	Node* tail;
	int size;
}Queue;
void Init(Queue* pq)
{
	assert(pq);
	pq->head = (Node*)malloc(sizeof(Node));
	pq->head->next = NULL;
	pq->tail = pq->head;
	pq->size = 0;
}
bool Empty(Queue* pq)
{
	assert(pq);
	if (pq->head->next == NULL)
		return true;
	return false;
}
void Push(Queue* pq, LData x)
{
	assert(pq);
	Node* new_node = (Node*)malloc(sizeof(Node));
	new_node->data = x;
	new_node->next = NULL;
	pq->tail->next = new_node;
	pq->tail = pq->tail->next;
	++pq->size;
}
void Pop(Queue* pq)
{
	assert(pq);
	assert(!Empty(pq));
	Node* tmp = pq->head;
	pq->head = pq->head->next;
	--pq->size;
	free(tmp);
	tmp = NULL;
}
int Size(Queue* pq)
{
	assert(pq);
	return pq->size;
}
LData Front(Queue* pq)
{
	assert(pq);
	assert(!Empty(pq));
	return pq->head->next->data;
}
void Print(Queue* pq)
{
	assert(pq);
	Node* cur = pq->head->next;
	while (cur != NULL)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
int main()
{
	Queue q;
	Init(&q);
	Push(&q, 1);
	Push(&q, 2);
	Push(&q, 3);
	Push(&q, 4);
	Print(&q);
	Push(&q, Front(&q));
	Pop(&q);
	Pop(&q);
	Pop(&q);
	Print(&q);
	printf("size = %d\n", Size(&q));
} 
 
循环队列 Circle Queue
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#define CAPACITY 5
typedef int QData;
typedef struct CircleQueue
{
	QData* data;
	int front;
	int tail;
}CircleQueue;
void Init(CircleQueue* pcq)
{
	assert(pcq);
	pcq->data = (QData*)malloc(sizeof(QData) * CAPACITY);
	pcq->front = pcq->tail = 0;
}
bool Empty(CircleQueue* pcq)
{
	assert(pcq);
	if (pcq->front == pcq->tail)
		return true;
	return false;
}
bool Full(CircleQueue* pcq)
{
	assert(pcq);
	if ((pcq->tail + 1) % CAPACITY == pcq->front)
		return true;
	return false;
}
void Push(CircleQueue* pcq, QData x)
{
	assert(pcq);
	assert(!Full(pcq));
	pcq->data[pcq->tail] = x;
	++pcq->tail;
	pcq->tail %= CAPACITY;
}
void Pop(CircleQueue* pcq)
{
	assert(pcq);
	assert(!Empty(pcq));
	++pcq->front;
	pcq->front %= CAPACITY;
}
void Print(CircleQueue* pcq)
{
	assert(pcq);
	int pos = pcq->front;
	while (pos != pcq->tail)
	{
		printf("%d ", pcq->data[pos]);
		++pos;
		pos %= CAPACITY;
	}
	printf("\n");
}
int main()
{
	CircleQueue cq;
	Init(&cq);
	Push(&cq, 1);
	Push(&cq, 2);
	Push(&cq, 3);
	Print(&cq);
	Pop(&cq);
	Pop(&cq);
	Print(&cq);
	Push(&cq, 1);
	Push(&cq, 2);
	Push(&cq, 3);
	Print(&cq);
	return 0;
} 
 
 
第三章 串
- 串是由零个或多个字符组成的顺序存储有限序列
 - 串的应用一般包含于<string.h>头文件,常用函数有strlen strcpy strcat strcmp
 
模拟实现string.h库函数
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
int MyStrlen(char* str)
{
	assert(str);
	int len = 0;
	char* cur = str;
	while (cur[len++] != '\0'){}
	return len - 1;
}
char* MyStrcpy(char* des, const char* src)
{
	assert(des);
	assert(src);
	char* tmp = des;
	while (*tmp++ = *src++) {}
	return des;
}
char* MyStrcat(char* des, const char* src)
{
	assert(des);
	assert(src);
	char* tmp = des;
	if (des == src)
	{
		int i = 0;
		int len = MyStrlen(src);
		while (*tmp++) {}
		--tmp;
		while (i < len)
		{
			*tmp++ = *src++;
			++i;
		}
		*tmp = '\0';
	}
	else
	{
		while (*tmp++) {}
		--tmp;
		while (*tmp++ = *src++) {}
	}
	return des;
}
int MyStrcmp(const char* str1, const char* str2)
{
	assert(str1);
	assert(str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		++str1;
		++str2;
	}
	return -(*str1 - *str2);
}
char* MyStrstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	char* cur = str1;
	while (*cur)
	{
		char* s1 = cur;
		char* s2 = str2;
		while (*s1 && *s2 && *s1 == *s2)
		{
			++s1;
			++s2;
		}
		if (!*s2)
			return cur;
		if (!*s1)
			return NULL;
		++cur;
	}
	return NULL;
}
int main()
{
	char str1[20] = "qwerdf";
	printf("strlen = %d\n", MyStrlen(str1));
	char str2[40];
	printf("str2 strcpy str1 = %s\n", MyStrcpy(str2, str1));
	printf("str2 strcat str1 = %s\n", MyStrcat(str2, str1));
	printf("str2 strcat str2 = %s\n", MyStrcat(str2, str2));
	char str3[40];
	printf("str3 strcmp str2 = %d\n", MyStrcmp(MyStrcpy(str3, str2), str2));
	printf("str1 strstr df = %s\n", MyStrstr(str1, "df"));
	return 0;
} 
 
 
第四章 广义表
- 广义表中的数据元素可以具有不同的结构(原子或列表),是链式存储结构
 - 广义表的第一个元素为表头,其余元素为表尾
 - 每个节点可分为表结点或原子节点,用标志位区分
 - 表结点包含标志域和指针域(头尾指针),原子节点包含标志域和值域
 - 广义表的深度为括号的重数,空表的深度为1,例如X = ((), (e), (a, (b,c,d)))的深度为3
 - 广义表的长度为包含的数据元素个数(一个子表只算一个元素)
 
广义表的实现 利用递归思想
代码转载于:广义表详解(C语言版)_红心火柴的博客-CSDN博客_广义表 c语言
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <malloc.h>
#define AtomType int	//原子类型
typedef enum
{
	HEAD,		//表头结点
	ATOM,		//原子结点
	CHILDLIST	//子表
}ElemTag;
//广义表结点
typedef struct GLNode
{
	ElemTag tag;
	union		//联合体
	{
		AtomType atom;
		struct GLNode* hp;	//表头指针
	};
	struct GLNode* tp;		//表尾指针
}GLNode;
typedef GLNode* GenList;	//广义表
void InitGenList(GenList& gl);
void CreateGenList(GenList& gl, char* str);
bool Sever(char* sub, char* hsub);
char* GetGenListStr(GenList gl, int type);
char* GetHead(GenList gl);
char* GetTail(GenList gl);
char* GetLast(GenList gl);
void ShowGenList(GenList gl);
bool GenListEmpty(GenList gl);
int GenListLength(GenList gl);
int GenListDepth(GenList gl);
void CopyGenList(GenList gl, GenList& T);
void InsertFirstGenList(GenList& gl, char* str);
void DeleteFirstGenList(GenList& gl, char*& str);
void ClearGenList(GenList& gl);
void DestroyGenList(GenList& gl);
//广义表初始化
void InitGenList(GenList& gl)
{
	gl = NULL;
}
//创建广义表:根据字符串创建
void CreateGenList(GenList& gl, char* str)
{
	int n = strlen(str);
	char* sub = (char*)malloc(sizeof(char) * (n - 2));	//存储表内元素
	char* hsub = (char*)malloc(sizeof(char) * (n - 2));	//存储表头
	assert(sub && hsub);
	strncpy(sub, str + 1, n - 2);	//除去字符串两端括号
	sub[n - 2] = '\0';
	if (gl == NULL)		//判空
	{
		gl = (GLNode*)malloc(sizeof(GLNode));
		gl->tag = HEAD;		//标记为头结点
		gl->hp = gl->tp = NULL;	//把子表指针和尾指针都置空
	}
	GLNode* p = gl;
	while (strlen(sub) != 0)
	{
		//尾插法,从后面插入结点
		//	1. 创建一个结点
		//  2. 让p所指节点的尾指针指向新建的结点
		//  3. 让p指向新建节点
		p = p->tp = (GLNode*)malloc(sizeof(GLNode));
		p->hp = p->tp = NULL;
		if (Sever(sub, hsub))	//Sever函数分离表头,并将表头存入hsub中
		{
			if (hsub[0] == '(')
			{
				//创建子表节点
				p->tag = CHILDLIST;	//标记子表
				CreateGenList(p->hp, hsub);
			}
			else
			{
				p->tag = ATOM;	//设置原子标记
				p->atom = atoi(hsub);	//将表头字符串转换成整型并赋值
			}
		}
	}
}
//分离表头,将sub中的表头存入到hsub中
bool Sever(char* sub, char* hsub)
{
	if (*sub == '\0' || strcmp(sub, "()") == 0)
	{
		hsub[0] = '\0';
		return true;
	}
	int n = strlen(sub);
	int i = 0;
	char ch = sub[0];
	int k = 0;	//表示括号信息
	while (i < n && (ch != ',' || k != 0))
	{
		if (ch == '(')
			++k;
		else if (ch == ')')
			--k;
		++i;
		ch = sub[i];
	}
	if (i < n)
	{
		//在 i 位置处截断
		sub[i] = '\0';
		strcpy(hsub, sub);			//将取得的表头放入hsub中
		strcpy(sub, sub + i + 1);	//更新sub:此时的sub是去掉表头hsub
	}
	else if (k != 0)		//判断内部括号是否匹配
		return false;
	else	// i >= n, 整个sub都是表头
	{
		strcpy(hsub, sub);
		sub[0] = '\0';
	}
	return true;
}
//将整数转换成字符串
void NumToStr(int num, char* str, int& i)
{
	char tmp[25];
	_itoa(num, tmp, 10);
	for (unsigned j = 0; j < strlen(tmp); ++j)
		str[i++] = tmp[j];
}
void GetGenList(GenList gl, char* str, int& i)
{
	GLNode* p = gl->tp;
	while (p != NULL)
	{
		if (p->tag == ATOM)
		{
			NumToStr(p->atom, str, i);
			if (p->tp != NULL)
				str[i++] = ',';
			p = p->tp;
		}
		else if (p->tag == CHILDLIST)
		{
			str[i++] = '(';
			GetGenList(p->hp, str, i);
			str[i++] = ')';
			if (p->tp != NULL)
				str[i++] = ',';
			p = p->tp;
		}
	}
}
//将广义表转化成字符串形式
char* GetGenListStr(GenList gl, int type)
{
	int i = 0;
	char* str = (char*)malloc(sizeof(char) * 1000);
	if (type == 0)
	{
		NumToStr(gl->atom, str, i);
	}
	else
	{
		GetGenList(gl, str, i);
	}
	str[i] = '\0';
	return str;
}
//取首元素
char* GetHead(GenList gl)
{
	if (gl->tp->tag == ATOM)
		return GetGenListStr(gl->tp, 0);
	else
		return GetGenListStr(gl->hp, 1);
}
char* GetTail(GenList gl)
{
	GLNode* p = gl->tp;
	if (p->tp != NULL)
		return GetGenListStr(p, 1);
	return NULL;
}
//取最后一个元素
char* GetLast(GenList gl)
{
	GLNode* p = gl->tp;
	while (p->tp != NULL)
		p = p->tp;
	if (p->tag == ATOM)
		return GetGenListStr(p, 0);
	else
		return GetGenListStr(p->hp, 1);
}
//打印广义表
void ShowGenList(GenList gl)
{
	printf("(%s)", GetGenListStr(gl, 1));
}
//判空
bool GenListEmpty(GenList gl)
{
	return gl->tp == NULL;
}
int GenListLength(GenList gl)
{
	int length = 0;
	GLNode* p = gl->tp;
	while (p != NULL)
	{
		++length;
		p = p->tp;
	}
	return length;
}
int GenListDepth(GenList gl)
{
	if (gl->tp == NULL)
		return 1;
	GLNode* p = gl->tp;
	int maxdepth = 0;
	int dep;
	while (p != NULL)
	{
		if (p != NULL)
		{
			if (p->tag == CHILDLIST)
			{
				dep = GenListDepth(p->hp);
				if (dep > maxdepth)
					maxdepth = dep;
			}
		}
		p = p->tp;
	}
	return maxdepth + 1;
}
//广义表复制
void CopyGenList(GenList gl, GenList& T)
{
	if (gl == NULL)
		return;
	if (T != NULL)
		DestroyGenList(T);
	T = (GLNode*)malloc(sizeof(GLNode));
	T->tag = gl->tag;
	T->hp = gl->hp;
	T->tp = gl->tp;
	GLNode* p = gl->tp;
	GLNode* q = T;
	while (p != NULL)
	{
		q = q->tp = (GLNode*)malloc(sizeof(GLNode));
		q->tag = p->tag;
		q->hp = q->tp = NULL;
		if (p->tag == ATOM)
		{
			q->atom = p->atom;
			p = p->tp;
		}
		else if (p->tag == CHILDLIST)
		{
			CopyGenList(p->hp, q->hp);
			p = p->tp;
		}
	}
}
//插入元素str
void InsertFirstGenList(GenList& gl, char* str)
{
	GenList t;
	InitGenList(t);
	CreateGenList(t, str);
	GLNode* p = t->tp;
	while (p->tp != NULL)
		p = p->tp;
	p->tp = gl->tp;
	gl->tp = t->tp;
	free(t);
}
//删除广义表第一个位置的元素
void DeleteFirstGenList(GenList& gl, char*& str)
{
	GenList t;
	InitGenList(t);
	t = gl->tp;
	gl->tp = gl->tp->tp;
	if (t->tag == CHILDLIST)
	{
		str = GetGenListStr(t->hp, 1);
		DestroyGenList(t->hp);
	}
	else if (t->tag == ATOM)
	{
		str = GetGenListStr(t, 0);
	}
	free(t);
}
//清空广义表
void ClearGenList(GenList& gl)
{
	GLNode* p = gl->tp;
	while (p != NULL)
	{
		if (p->tag == ATOM)
		{
			gl->tp = p->tp;
			free(p);
			p = gl->tp;
		}
		else if (p->tag == CHILDLIST)
		{
			ClearGenList(p->hp);
			p = p->tp;
		}
	}
}
//销毁
void DestroyGenList(GenList& gl)
{
	ClearGenList(gl);
	free(gl);
	gl = NULL;
}
//测试
int main()
{
	GenList gl;
	InitGenList(gl);
	char ga[30] = "(1,2,3)";
	char gb[30] = "(1,(2,3))";
	char gc[30] = "(1,(2,3),4)";
	char gd[30] = "((1,2),3)";
	char ge[30] = "((1,2,3))";
	char gf[30] = "()";
	char gg[30] = "(1,(2,(3,(10,20),4),5),6)";
	char gh[30] = "((((1,2),1),1),6,1)";
	CreateGenList(gl, gg);
	ShowGenList(gl);
	printf("\n");
	int length = GenListLength(gl);
	printf("length = %d\n", length);
	int depth = GenListDepth(gl);
	printf("depth = %d\n", depth);
	GenList T;
	InitGenList(T);
	printf("----------------------------\n");
	printf("复制:");
	CopyGenList(gl, T);
	ShowGenList(T);
	printf("\n");
	printf("----------------------------\n");
	printf("插入前:");
	ShowGenList(gl);
	printf("\n");
	InsertFirstGenList(gl, ga);
	printf("插入后:");
	ShowGenList(gl);
	printf("\n");
	printf("----------------------------\n");
	char* str;
	printf("删除前:");
	ShowGenList(gl);
	printf("\n");
	DeleteFirstGenList(gl, str);
	printf("删除后:");
	ShowGenList(gl);
	printf("\n");
	printf("删除的首元素为:%s\n", str);
	printf("----------------------------\n");
	ShowGenList(gl);
	printf("\n");
	printf("头元素为:%s\n", GetHead(gl));
	printf("尾元素为:%s\n", GetTail(gl));
	DestroyGenList(gl);
	return 0;
} 
 
 
第五章 树
- 树是有n个节点的有限集,任意一棵非空树有且仅有一个根节点
 - 拥有的分支数称为节点的度,度为0的节点为叶子节点
 - 森林是若干棵互补相交的树的集合
 
二叉树:
- 二叉树每个节点的度 ≤ 2,分左右孩子
 - 二叉树的第 i 层最多有 2^(i-1) 个节点
 - 当深度为 k 的二叉树有 2^k - 1 个节点时是满二叉树
 - 当k-1层是满的,第k层是连续的节点时是完全二叉树
 - 具有 n 个节点的满二叉树的深度 h = log2(n+1)
 - 任一二叉树,设叶节点数为n0,度为2的节点数为n2,则 n0 = n2 + 1
 - 遍历二叉树分为先序遍历(根左右)、中序遍历(左根右)、后序遍历(左右根)、层序遍历
 - 任一具有n个节点的树,其分支有n-1条
 
 二叉树与森林的转换:
赫夫曼树:
- 赫夫曼树,又称最优树,是一类带权路径最短的树
 - 路径长度:路径上的分支数目
 - 树的带权路径长度:所有叶子节点的带权路径长度之和
 

二叉树的构建与遍历 递归思想
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef char NodeType;
typedef struct BNTree
{
	NodeType data;
	struct BNTree* left;
	struct BNTree* right;
}BNTree;
BNTree* BuyNode(NodeType d)			//开辟一个二叉树节点
{
	BNTree* new_node = (BNTree*)malloc(sizeof(BNTree));
	new_node->data = d;
	new_node->left = new_node->right = NULL;
	return new_node;
}
BNTree* CreateBNTree(NodeType* d, int* pi)		//二叉树创建
{
	if (d[*pi] = '#')
	{
		(*pi)++;
		return NULL;
	}
	BNTree* root = BuyNode(d[(*pi)++]);			//先序构建
	root->left = CreateBNTree(d, pi);
	root->right = CreateBNTree(d, pi);
	return root;
}
void PreOrder(BNTree* root)	//先序遍历,中序遍历和后续遍历都是一样的递归思想
{
	if (root == NULL)
		return;
	printf("%c ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
} 
 
第六章 图
图是一种抽象数据类型,图的元素被称为顶点,顶点之间可能存在相关关系,一个图G包含一个顶点集V和关系集VR,记为:G = (V, VR)
图的常用术语
1. 有向图、弧
- 若图G中的两个顶点a、b的关系为有序对<a,b>,<a,b>∈VR,则称<a,b>为有向图G的一条弧
 - 在弧<a,b>中,a是弧尾(初始点),b是弧头(终止点)
 - 有向图是由顶点集和弧集组成的二元集合

 
2. 无向图、边
- 若图G中的两个顶点a、b的关系为无序对(a,b),(a,b)∈VR,则称(a,b)为无向图G的一条边
 - 若顶点a、b间有边,则(a,b)表示a、b互为邻接点,边(a,b)依附于顶点a、b

 
3. 完全图、有向完全图
- n:顶点的数目
 - e:边或弧的数目
 - 若边数 e = n*(n-1)/2 的无向图,则称为完全图,表示任意两个顶点间都有边
 - 若弧数 e = n*(n-1) 的有向图,则称有向完全图,表示任意两个顶点间都有两条弧
 

4. 网
- 弧(边)上加权的图,分为有向网和无向网
 - 故有四种类型的图:有向图、无向图、有向网、无向网
 
5. 子图
- 对图G=(V, VR),G1=(V1, VR1),若V1∈V且VR1∈VR,则G1是G的子图
 - 下例中G1、G2、G3都是G的子图,G4不是G的子图
 

6. 度、出度、入度
- 度D(TD):图中与某顶点相关的边(弧)的数目,称为某顶点的度
 - 无向图中某顶点的度表示该顶点有多少个邻接点
 - 出度OD:有向图中以某顶点为弧尾的弧的数目
 - 入度ID:有向图中以某顶点为弧头的弧的数目
 - 有向图某顶点的度 D = OD + ID
 
7. 连通性
- 从顶点vi到顶点vj有路径,则称二者是连通的
 - 连通图:图中任意两个顶点都是连通的
 - 完全图一定是连通图,连通图不一定是完全图
 - 连通分量:无向图的极大连通子图,连通图的连通分量是自己本身

 - 强连通图:有向图中任意两个顶点vi和vj之间,从vi到vj和从vj到vi之间都存在路径
 - 强连通分量:有向图的极大强连通子图,强联通图的强连通分量是自己本身

 
8. 生成树
- 一个连通图的生成树是一个极小连通子图
 - 生成树含有图中的全部n个顶点,但有且仅有n-1条边
 - 如果在生成树上再添加一条边,必定构成一个环,不再是树结构
 - 一个有n个顶点的图,如果边数小于n-1,则一定是非连通图,如果多于n-1条边,则一定有环
 - 有n-1条边的图不一定是生成树
 - 一个连通图可以有多颗生成树
 
图的存储结构
1. 数组表示
- 用两个数组分别存储数据元素的信息和数据元素之间的关系
 - 顶点数组:用一维数组存储顶点
 - 关系数组:用二维数组存储顶点之间的关系
 
(1)无向图
- 顶点数组vexs:V = {v0, v1, v2, v3}
 - 关系数组(邻接矩阵arcs):1表示顶点之间有关系,0表示顶点之间无关系
 - 邻接矩阵arcs:VR = {(v0,v2), (v0,v3), (v2,v3)}
 - 无向图的邻接矩阵是对称矩阵
 - 顶点 vi 的度为第 i 行或第 i 列的和
 
(2)有向图
- 顶点数组vexs:V = {v0, v1, v2, v3}
 - 邻接矩阵arcs:VR = {<v0,v2>, <v2,v0>, <v2,v3>, <v3,v0>}
 - 有向图的邻接矩阵不一定是对称矩阵
 - 顶点 vi 的度为第 i 行和第 i 列的和,即 D = OD + ID
 
2. 邻接表
顺序+链式存储结构,通过头结点数组保存顶点信息,用单链表保存顶点之间的关系

无向图:
- 图G的每个顶点建立一个单链表,第 i 个单链表中的结点表示依附于 vi 的边
 - 若有 n 个顶点和 c 条边,则有 n 条单链表和 2*c 个表结点
 - 顶点 vi 的度 = 第 i 个单链表的长度
 
有向图:
- 图G中的每个顶点建立一个单链表,第 i 条链表中的表结点值为 j ,表示<vi, vj>
 - 若有 n 个顶点和 c 条弧,则有 n 条单链表和 c 个表结点
 - 顶点 vi 的度 = 第 i 个单链表的长度
 
3. 十字链表
十字链表是针对有向图设计的邻接表和逆邻接表的组合


- 每个顶点有一个顶点结点,顶点结点包括:data(顶点信息)、first_in(指向以该顶点为弧头的第一条弧)、first_out(指向以该顶点为弧尾的第一条弧)
 - 每条弧有一个弧结点,若 vi 到 vj 有弧,则弧结点包括:tail_vec(弧尾位置)、head_vec(弧头位置)、h_link(指向下一条弧头(vi)相同的弧)、t_link(指向下一条弧尾(vi)相同的弧)
 - n条弧就有n个链表结点
 
4. 邻接多重表(无向图)
- 每个顶点有一个头结点,顶点节点包括:data(顶点信息)、first_edge(指向第一条依附于该顶点的边)
 - 每一条边都有一个表结点,表结点包括:mark(标志域,标记该边是否被搜索过)、vi / vj(依附于该边的两个顶点位置)、li(指向下一条依附于顶点vi的边)、lj(指向下一条依附于顶点vj的边)
 - n条边就有n个表结点
 

第七章 查找
折半查找(二分查找):针对有序数列,时间复杂度为O(log2N)
二叉排序树(二叉查找树):若左子树非空,则左子树上所有杰点都小于它的根结点;若右子树非空,则右子树上所有结点都大于它的根结点
平衡二叉树(AVL树):左子树和右子树的深度之差的绝对值(平衡因子)不超过1,某结点的平衡因子为其左子树的深度减去右子树的深度
二叉排序树的构建:
#include <iostream>
#include <queue>
using namespace std;
typedef struct Node
{
	int data;
	struct Node* left;
	struct Node* right;
}Node;
void BuyNode(Node*& t, int x)
{
	t = new Node;
	t->data = x;
	t->left = t->right = NULL;
}
void CreateBTree(Node*& t, int x)
{
	if (t == NULL)
	{
		BuyNode(t, x);
		return;
	}
	if (x < t->data)
		CreateBTree(t->left, x);
	else
		CreateBTree(t->right, x);
}
void PrintBTree(Node* t)
{
	Node* p = t;
	queue<Node*> q;
	q.push(p);
	while (!q.empty())
	{
		p = q.front();
		q.pop();
		cout << p->data << ' ';
		if (p->left != NULL)
			q.push(p->left);
		if (p->right != NULL)
			q.push(p->right);
	}
}
int main()
{
	int n;
	while (cin >> n)
	{
		Node* t = NULL;
		for (int i = 0; i < n; ++i)
		{
			int a;
			cin >> a;
			CreateBTree(t, a);
		}
		PrintBTree(t);
		cout << endl;
	}
} 
 
B-树(平衡多路查找树):常应用于文件系统,一棵m阶的B-树,或为空树,或遵循下列条件:
- 树中的每个结点最多有m棵子树
 - 若根结点不是叶子结点,则至少有两棵子树
 - 除根结点外,所有非终端结点至少有 m/2 棵
 - 所有非终端结点包含如下信息:(n, A0,K1, A1, K2, A2......Kn, An), Ki为关键码
 - 所有叶子结点都出现在同一层次上,且不带信息
 

要在如上4阶B-树中查找45这个树的步骤:
- 首先从根结点开始,45 > 35,往右子树走,到结点c
 - 结点c有两个关键字,43 < 45 < 78,故往中间的子树走,到结点g
 - 结点g有三个关键字,45 < 47,往最左边子树走,到叶子结点F,故45不在B-树中
 
B-树的插入:
- 用查找的方法找出关键字的插入位置,若查找到了这个数,则直接返回
 - 判断插入位置的结点是否满足 n <= m-1 ,若满足则直接插入,若不满足则进行分裂
 - 分裂规则是左右两边的数分裂成不同结点,中间的数移到父结点,再检查父结点是否满足阶数规则
 
B-树的删除:
- 先直接删除,再视情况进行调整,以满足阶数规则
 
B+树:
一棵m阶的B+树与m阶的B-树的差异:
- 有n棵子树的结点有n个关键字
 - 所有关键字信息都在叶子节点中,叶子结点本身依关键字顺序存储
 - 所有非终端结点都是索引部分,结点中只含有其子树(根结点)最大(或最小)的关键字
 - B+树的查找,不管成功与否,每次都是从根到叶子结点的路径
 

哈希表:若结构中存在关键字和K相等的记录,则必定在 f(K) 的存储位置上,由此不需比较便可直接取得所查记录,因此这个对应关系 f 为哈希函数,这个思维建立的表为哈希表
第八章 内排序
直接插入排序
void InsertSort(int* arr, int n)
{
	int i;
	for (i = 0; i < n - 1; ++i)
	{
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0 && tmp < arr[end])
		{
			arr[end + 1] = arr[end];
			--end;
		}
		arr[end + 1] = tmp;
	}
} 
 
希尔排序
第一步:预排序
第二部:直接插入排序
void ShellSort(int* arr, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		int i;
		for (i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = arr[end + gap];
			while (end >= 0 && tmp < arr[end])
			{
				arr[end + gap] = arr[end];
				end -= gap;
			}
			arr[end + gap] = tmp;
		}
	}
} 
 
直接选择排序
void SelectSort(int* arr, int n)
{
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
		int mini = begin;
		int maxi = end;
		int i;
		for (i = begin + 1; i <= end; ++i)
		{
			if (arr[i] > arr[maxi])
				maxi = i;
			else if (arr[i] < arr[mini])
				mini = i;
		}
		int tmp = arr[begin];
		arr[begin] = arr[mini];
		arr[mini] = tmp;
		if (maxi == begin)
			maxi = mini;
		int temp = arr[end];
		arr[end] = arr[maxi];
		arr[maxi] = temp;
		++begin;
		--end;
	}
} 
 
堆排序
//建立大根堆——升序
//建立小根堆——降序
void AdjustDown(int* arr, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && arr[child + 1] > arr[child])
			++child;
		if (arr[parent] < arr[child])
		{
			int tmp = arr[parent];
			arr[parent] = arr[child];
			arr[child] = tmp;
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* arr, int n)
{
	int parent = (n - 1 - 1) / 2;
	while (parent >= 0)
	{
		AdjustDown(arr, n, parent);
		--parent;
	}
	while (n > 1)
	{
		int tmp = arr[0];
		arr[0] = arr[n - 1];
		arr[n - 1] = tmp;
		AdjustDown(arr, n - 1, 0);
		--n;
	}
} 
 
冒泡排序
void BubbleSort(int* arr, int n)
{
	int i, j;
	for (i = 0; i < n; ++i)
	{
		int flag = 0;
		for (j = 0; j < n - 1 - i; ++j)
		{
			if (arr[j + 1] < arr[j])
			{
				int tmp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = tmp;
				flag = 1;
			}
		}
		if (flag == 0)
			break;
	}
} 
 
快速排序
第一步:找到一个值key(默认将key值设为数组第一个元素)
第二步:用循环将比key值小的放key值左边,比key值大的放key值右边
第三步:用递归(二叉树的分治算法),将key值的左右分为两个子树进行排序
//三数取1,取中位数
int GetMidIndex(int* arr, int begin, int end)
{
	int midi = (begin + end) / 2;
	if (arr[begin] < arr[end])
	{
		if (arr[midi] < arr[begin])
			return begin;
		else if (arr[midi] < arr[end])
			return midi;
		else
			return end;
	}
	else
	{
		if (arr[midi] > arr[begin])
			return begin;
		else if (arr[midi] > arr[end])
			return midi;
		else
			return end;
	}
}
int PtrPartSort(int* arr, int begin, int end)
{
	int keyi = begin;
	int pre = begin;
	int cur = begin + 1;
	int midi = GetMidIndex(arr, begin, end);
	int tmp = arr[keyi];
	arr[keyi] = arr[midi];
	arr[midi] = tmp;
	while (cur <= end)
	{
		if (arr[cur] < arr[keyi] && pre++ != cur)
		{
			int temp = arr[cur];
			arr[cur] = arr[pre];
			arr[pre] = temp;
		}
		++cur;
	}
	int t = arr[keyi];
	arr[keyi] = arr[pre];
	arr[pre] = t;
	return pre;
}
void QuickSort(int* arr, int begin, int end)
{
	if (begin >= end)
		return;
	int keyi = PtrPartSort(arr, begin, end);
	if (begin - end > 10)
	{
		QuickSort(arr, begin, keyi - 1);
		QuickSort(arr, keyi + 1, end);
	}
	else
	{
		InsertSort(arr + begin, end - begin + 1);
	}
} 
 
归并排序
void _MergeSort(int* arr, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;
	int mid = (begin + end) / 2;
	_MergeSort(arr, begin, mid, tmp);
	_MergeSort(arr, mid + 1, end, tmp);
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] < arr[begin2])
			tmp[i++] = arr[begin1++];
		if (arr[begin1] > arr[begin2])
			tmp[i++] = arr[begin2++];
	}
	while (begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}
	memcpy(arr + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* arr, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	_MergeSort(arr, 0, n - 1, tmp);
	free(tmp);
} 
 
计数排序
void NumSort(int* arr, int n)
{
	int min = arr[0];
	int max = arr[0];
	int i;
	for (i = 1; i < n; ++i)
	{
		if (arr[i] < min)
			min = arr[i];
		if (arr[i] > max)
			max = arr[i];
	}
	int* count = (int*)malloc(sizeof(int) * (max - min + 1));
	memset(count, 0, sizeof(int) * (max - min + 1));
	for (i = 0; i < n; ++i)
	{
		count[arr[i] - min]++;
	}
	int j = 0;
	for (i = 0; i <= max - min; ++i)
	{
		while (count[i]--)
		{
			arr[j++] = min + i;
		}
	}
	free(count);
	count = NULL;
} 
 
基数排序
#include <iostream>
#include <queue>
#include <iomanip>
using namespace std;
#define K 3
#define RADIX 10
int GetKey(int value, int k)
{
	int key = 0;
	while (k >= 0)
	{
		key = value % 10;
		value /= 10;
		--k;
	}
	return key;
}
queue<int> Q[RADIX];	//定义基数
void Distribute(int* arr, int left, int right, int k)
{
	for (int i = left; i < right; ++i)
	{
		int key = GetKey(arr[i], k);
		Q[key].push(arr[i]);
	}
}
void Collect(int* arr)
{
	int k = 0;
	for (int i = 0; i < RADIX; ++i)
	{
		while (!Q[i].empty())
		{
			arr[k++] = Q[i].front();
			Q[i].pop();
		}
	}
}
void RadixSort(int* arr, int left, int right)
{
	for (int i = 0; i < K; ++i)
	{
		Distribute(arr, left, right, i);
		Collect(arr);
	}
}
void PrintArr(int* arr, int n)
{
	int i = 0;
	for (; i < n; ++i)
		printf("%d ", arr[i]);
	printf("\n");
}
int main()
{
	int arr[20] = { 11, 333, 521, 71, 19, 2, 432, 61, 38, 120, 0 };
	RadixSort(arr, 0, 11);
	PrintArr(arr, 11);
	return 0;
} 
                

















