数据结构代码小总:链表 栈 队列 树

LinkList.h


#pragma once
#include 
#include 
#define TRUE 1
#define FALSE 0

typedef int ElemType;//假定线性表的元素类型为整型

//定义节点
typedef struct node {
	int data;
	struct node *pNext;
}LinkListNode;

///创建带有头节点的链表Init
///函数的返回值是头节点,没有参数
LinkListNode* InitLinkList(void);
///求长度:求顺序表中的元素的个数
///函数的返回值是顺序表的长度,参数pHead:是单链表的头节点
int GetSizeLinkList(LinkListNode* pHead);
///取元素:取给定位置的元素值
///返回值:第i个元素的地址,pHead:头指针,i 待查节点的序号
LinkListNode* GetLinkListNode(LinkListNode* pHead, int pos);

///查元素:查找给定元素值的位置
///返回值:节点的地址,找不到就返回NULL
///pHead:单链表的头指针,objData:是需要匹配的元素值
LinkListNode* LocateLinkList(LinkListNode* pHead, int objData);

///插入元素:在指定的位置插入给定的值
///尾插法建立单链表(将逻辑上的顺序表放入单链表的物理结构当中)
///返回值:链表的头指针,arr:传入的顺序表,length:顺序表的长度
LinkListNode* Create_Rear_LkList(ElemType arr[], int length);

///头插法建立单链表
///返回值:链表的头指针,arr:传入的顺序表,length:顺序表的长度
LinkListNode* Create_Front1_LkList(ElemType arr[], int length);
///头插法2
///返回值:链表的头指针,arr:传入的顺序表,length:顺序表的长度
LinkListNode* Create_Front2_LkList(ElemType arr[], int length);

///头插法建立
///返回值:链表的头指针,arr:传入的顺序表,length:顺序表的长度
LinkListNode* Create_Front3_LkList(ElemType arr[], int length);


///插入元素:在指定的位置插入给定的值
//ptr:带插入的元素位置,将在ptr的后继结点中插入,x:插入的值
void Insert_After_LkList(LinkListNode* ptr, ElemType x);

///指定位置之前插入
///pHead:链表的头指针,ptr:带插入的元素位置,x:插入的值
void Insert_Before_LkList(LinkListNode* pHead, LinkListNode* ptr, ElemType x);


///删除节点:Ptr是需要删除的节点,将删除ptr的后续节点
///返回值是带删除的节点位置
LinkListNode* Delete_After_LkList(LinkListNode* ptr);

///删除第i个节点
///返回值是带删除的节点位置,pHead:头节点,i是第i个元素
LinkListNode* Delete_i_LkList(LinkListNode* pHead, int i);

///遍历
void ShowLkList(LinkListNode* pHead);

SeqList.h


#pragma once
#include 
#include 
typedef int ElemType;//假定线性表的元素类型为整型
#define LIST_SIZE 1024 //假定我们的线性表长度是1024
#define TRUE 1
#define FALSE 0
typedef struct {
	ElemType data[LIST_SIZE];
	int last;//指向最后一个节点的位置
}SequenList;



//last的存在的目的:为了在函数调用的时候传递数据方便,因为我们与分配
//的空间中,并不是立即存满的

///数组顺序表的实现初始化
///获得一个长度为0的数组
SequenList* InitSeq();


///求长度:求线性表中的元素的个数
///获得当前数组顺序表的元素个数,pList:顺序表的起始地址
int GetSizeSeq(SequenList* pList);

///取元素:取给定位置的元素值
///返回值是元素值
///pList:目标的顺序表, pos:获取元素的下标 ,e:将元素值放入
int GetElemSqList(SequenList* pList, int pos, ElemType *e);

///	查元素:查找给定元素值的位置
///相同值只去第一个
///返回值:-1表示没有找到,否则返回带查找元素的角标
///pList:传入的数组顺序表,Key比对的值
int LocateElemSqList(SequenList* pList, ElemType key);

///	插入元素:在指定的位置插入给定的值
/// 插入的位置为k:k: 0 -- n-1
//pList:目标顺序表,x待插入的元素,k插入的位置
int InsertElemSqList(SequenList* pList, ElemType x, int k);

///删除:删除指定位置的值或者是删除给定的值。
//pList:目标顺序数组,k表示需要删除的位置
int DelElemSqList(SequenList *pList, int k);

///遍历元素:从头到尾扫描线性表。
///pList:目标顺序数组
void showSeqList(SequenList* pList);

Linux内核中的双向链表.h


//#ifndef _LIST_HEAD_H
//#define _LIST_HEAD_H
//
 双向链表节点
//struct list_head {
//	struct list_head *next, *prev;
//};
//
 初始化节点:设置name节点的前继节点和后继节点都是指向name本身。
//#define LIST_HEAD_INIT(name) { &(name), &(name) }
//
 定义表头(节点):新建双向链表表头name,并设置name的前继节点和后继节点都是指向name本身。
//#define LIST_HEAD(name) \
//    struct list_head name = LIST_HEAD_INIT(name)
//
 初始化节点:将list节点的前继节点和后继节点都是指向list本身。
//static inline void INIT_LIST_HEAD(struct list_head *list)
//{
//	list->next = list;
//	list->prev = list;
//}
//
 添加节点:将new插入到prev和next之间。
//static inline void __list_add(struct list_head *new,
//	struct list_head *prev,
//	struct list_head *next)
//{
//	next->prev = new;
//	new->next = next;
//	new->prev = prev;
//	prev->next = new;
//}
//
 添加new节点:将new添加到head之后,是new称为head的后继节点。
//static inline void list_add(struct list_head *new, struct list_head *head)
//{
//	__list_add(new, head, head->next);
//}
//
 添加new节点:将new添加到head之前,即将new添加到双链表的末尾。
//static inline void list_add_tail(struct list_head *new, struct list_head *head)
//{
//	__list_add(new, head->prev, head);
//}
//
 从双链表中删除entry节点。
//static inline void __list_del(struct list_head * prev, struct list_head * next)
//{
//	next->prev = prev;
//	prev->next = next;
//}
//
 从双链表中删除entry节点。
//static inline void list_del(struct list_head *entry)
//{
//	__list_del(entry->prev, entry->next);
//}
//
 从双链表中删除entry节点。
//static inline void __list_del_entry(struct list_head *entry)
//{
//	__list_del(entry->prev, entry->next);
//}
//
 从双链表中删除entry节点,并将entry节点的前继节点和后继节点都指向entry本身。
//static inline void list_del_init(struct list_head *entry)
//{
//	__list_del_entry(entry);
//	INIT_LIST_HEAD(entry);
//}
//
 用new节点取代old节点
//static inline void list_replace(struct list_head *old,
//	struct list_head *new)
//{
//	new->next = old->next;
//	new->next->prev = new;
//	new->prev = old->prev;
//	new->prev->next = new;
//}
//
 双链表是否为空
//static inline int list_empty(const struct list_head *head)
//{
//	return head->next == head;
//}
//
 获取"MEMBER成员"在"结构体TYPE"中的位置偏移
//#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
//
 根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针
//#define container_of(ptr, type, member) ({          \
//    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
//    //(type *)( (char *)__mptr - offsetof(type,member) );})
//
 遍历双向链表
//#define list_for_each(pos, head) \
//    for (pos = (head)->next; pos != (head); pos = pos->next)
//
//#define list_for_each_safe(pos, n, head) \
//    for (pos = (head)->next, n = pos->next; pos != (head); \
//        pos = n, n = pos->next)
//
//#define list_entry(ptr, type, member) \
//    container_of(ptr, type, member)
//
//#endif

 

数组形式的顺序表



#include "SeqList.h"
/*
1)	初始化:给线性表中的相关参数赋值
2)	求长度:求线性表中的元素的个数
3)	取元素:取给定位置的元素值
4)	查元素:查找给定元素值的位置
5)	插入元素:在指定的位置插入给定的值
6)	删除:删除指定位置的值或者是删除给定的值。
7)	遍历元素:从头到尾扫描线性表。

*/
SequenList* lPtr;
//实现初始化
SequenList* InitSeq() {
	SequenList *pList=NULL;
	pList = (SequenList *)malloc(sizeof(SequenList));
	if (pList != NULL)
		pList->last = 0;//初始化成功,且长度为0

	return pList;
}
//SequenList seqenList;
//SequenList *seqenList;
//前者分配空间了,而后者没有空间


//求长度:求线性表中的元素的个数
int GetSizeSeq(SequenList* pList) {
	return pList->last;
}

//取元素:取给定位置的元素值
/// pList:目标的顺序表, pos:获取元素的下标 ,e:将元素值放入
int GetElemSqList(SequenList* pList, int pos, ElemType *e) {
	if (pos<0 || pos>pList->last) {
		return FALSE;
	}
	if (pList->last <= 0)
		return FALSE;
	//说明此时pos在0 -- n之间
	*e = pList->data[pos];
	return TRUE;
}

//	查元素:查找给定元素值的位置
//相同值只去第一个
//返回值:-1表示没有找到,否则返回带查找元素的角标
//pList:传入的数组顺序表,Key比对的值
int LocateElemSqList(SequenList* pList, ElemType key) {
	int i;
	for (i = 0;i < pList->last;i++) {
		if (pList->data[i] == key)
			return i;
	}
	return -1;
}

//	插入元素:在指定的位置插入给定的值
// 插入的位置为k:k: 0 -- n-1
// 顺序表:不满
//pList:目标顺序表,x待插入的元素,k插入的位置
int InsertElemSqList(SequenList* pList, ElemType x, int k) {
	int j;
	//顺序表尚未填满
	if (pList->last >= LIST_SIZE - 1)
		return FALSE;
	//表明K是有效位置
	if (k<0 || k>(pList->last + 1))
		return FALSE;
	for (j = pList->last;j >= k;j--) {
		pList->data[j + 1] = pList->data[j];
	}
	pList->data[k] = x;
	pList->last = pList->last + 1;
	return TRUE;
}

//删除:删除指定位置的值或者是删除给定的值。
//pList:目标顺序数组,k表示需要删除的位置
int DelElemSqList(SequenList *pList, int k) {
	if ((k >= 0 && k <= pList->last) && (pList->last != 0)) {
		for (int j = k;j <= pList->last;j++) {
			pList->data[j] = pList->data[j + 1];
		}
		pList->last--;
		return TRUE;
	}
	return FALSE;
}

//遍历元素:从头到尾扫描线性表。
void showSeqList(SequenList* pList) {
	for (int i = 0;i < pList->last;i++) {
		printf(" %d", pList->data[i]);
	}
}


int main1(void) {
	lPtr = InitSeq();//这样是否可以=>可以的,且使用NULL来判断是否初始化完毕
	if (lPtr) {
		//todo:继续使用这个顺序表
		for (int i = 0;i < 10;i++) {
			InsertElemSqList(lPtr, i, i);
		}
		printf("初始化后顺序表的元素个数:%d", GetSizeSeq(lPtr));
		printf("************\n");
		showSeqList(lPtr);
		InsertElemSqList(lPtr, 2000, 0);
		printf("初始化后顺序表的元素个数:%d", GetSizeSeq(lPtr));
		printf("************\n");
		showSeqList(lPtr);
		DelElemSqList(lPtr, 1);
		printf("初始化后顺序表的元素个数:%d", GetSizeSeq(lPtr));
		printf("************\n");
		showSeqList(lPtr);
		int pos = LocateElemSqList(lPtr, 16);
		if (pos >= 0) {
			printf("当前元素位于%d", pos);
		}
		else {
			printf("没有找到这个元素");
		}
		printf("************\n");
		showSeqList(lPtr);
		
	}
	else {
		//todo:提示没有可以使用的空间
	}
	getchar();
	getchar();
	return 0;
}

 

链表形式的顺序表



#include "LinkList.h"

/*
1)	初始化:给线性表中的相关参数赋值
2)	求长度:求线性表中的元素的个数
3)	取元素:取给定位置的元素值
4)	查元素:查找给定元素值的位置
5)	插入元素:在指定的位置插入给定的值
6)	删除:删除指定位置的值或者是删除给定的值。
7)	遍历元素:从头到尾扫描线性表。

*/

///创建带有头节点的链表Init
LinkListNode* InitLinkList(void) {
	LinkListNode* pHead = NULL;
	pHead = (LinkListNode*)malloc(sizeof(LinkListNode));
	if (pHead) {
		pHead->pNext = NULL;
	}
	return pHead;
}
//求长度:求线性表中的元素的个数
int GetSizeLinkList(LinkListNode* pHead) {
	int n = 0;
	while (pHead->pNext) {
		n++;
		pHead = pHead->pNext;
	}
	return n;
}

//取元素:取给定位置的元素值
//输入链表的头指针,要查找的编号,输出第i个元素的地址
///pHead:头指针,i 待查节点的序号
LinkListNode* GetLinkListNode(LinkListNode* pHead, int pos) {
	int j;
	LinkListNode* p;
	p = pHead;
	j = 0;
	if(pos == 0)
		return NULL;
	while (j < pos && p->pNext != NULL) {
		p = p->pNext;
		j++;
	}
	if (pos == j)
		return p;
	else
		return NULL;
}

//查元素:查找给定元素值的位置
///找到就返回节点的地址,找不到就返回NULL
LinkListNode* LocateLinkList(LinkListNode* pHead, int objData) {
	LinkListNode* p;
	p = pHead->pNext;//跳过头节点
	while (p != NULL && p->data != objData) {
		p = p->pNext;
	}
	return p;

}

//插入元素:在指定的位置插入给定的值
//因为链表这种结构的内存是由程序员管理的,因此它的建立有一定的运算
//方法
///尾插法建立单链表(将逻辑上的顺序表放入单链表的物理结构当中)
//arr:传入的顺序表,length:顺序表的长度
LinkListNode* Create_Rear_LkList(ElemType arr[], int length) {
	LinkListNode* pHead, *p, *q;
	int i;//循环变量,用来遍历全部的顺序表

	pHead = (LinkListNode*)malloc(sizeof(LinkListNode));

	q = pHead; //q是获得了当前链表的头节点
	//q保存了pHead,同时通过q不断前移使得链表串联起来
	for (i = 0;i < length;i++) {
		//获得一个单链表的节点,将这个节点加入到有
		//pHead指向的这个链表当中
		p = (LinkListNode*)malloc(sizeof(LinkListNode));
		//p获得一个可以加入链表的节点单元
		p->data = arr[i];//将顺序表的内容存入单链表的节点

		q->pNext = p;//
		q = p;
	}
	p->pNext = NULL;
	return pHead;

}

///头插法建立单链表
LinkListNode* Create_Front1_LkList(ElemType arr[], int length) {
	LinkListNode* pHead, *p, *q;
	int i;
	pHead = (LinkListNode*)malloc(sizeof(LinkListNode));
	pHead->pNext = NULL;
	q = pHead->pNext;
	//头插的时候,必须逆序遍历顺序表
	for (i = length - 1;i >= 0;i--) {
		p = (LinkListNode*)malloc(sizeof(LinkListNode));
		p->data = arr[i];
		p->pNext = q;//是的新加入的节点传入了上一个节点
		pHead->pNext = p;//头节点指向了当前的新加入节点
		q = pHead->pNext;//让q指向当前的节点
	}
	return pHead;
}
///头插法2
LinkListNode* Create_Front2_LkList(ElemType arr[], int length) {
	LinkListNode* pHead, *p, *q;
	//p是新加入节点,q是当前节点
	int i;
	q = NULL;
	for (i = length - 1;i >= 0;i--) {
		p = (LinkListNode*)malloc(sizeof(LinkListNode));
		p->data = arr[i];
		p->pNext = q;
		q = p;
	}
	pHead = (LinkListNode*)malloc(sizeof(LinkListNode));
	pHead->pNext = q;
	return pHead;
}

///头插法建立
LinkListNode* Create_Front3_LkList(ElemType arr[], int length) {
	LinkListNode* pHead, *p;
	int i;
	pHead = (LinkListNode*)malloc(sizeof(LinkListNode));
	pHead->pNext = NULL;
	for (i = length - 1;i >= 0;i--) {
		p = (LinkListNode*)malloc(sizeof(LinkListNode));
		p->data = arr[i];
		p->pNext = pHead->pNext;
		pHead->pNext = p;
	}
	//之所以我们的方法三可以节省方法一中的一个变量q
	//原因是:pHead不发生变化,而pHead中的pNext始终作为当前节点的指针
	return pHead;
}

/*
顺序表:12,33,44,76,89,90(逻辑上的顺序表)=>单链表
本例中,我们用数组表示这种顺序表
*/


//插入元素:在指定的位置插入给定的值
//在指定位置之后插入
void Insert_After_LkList(LinkListNode* ptr, ElemType x) {
	LinkListNode* s;
	s = (LinkListNode*)malloc(sizeof(LinkListNode));
	s->data = x;
	s->pNext = ptr->pNext;
	ptr->pNext = s;
}

//指定位置之前插入
void Insert_Before_LkList(LinkListNode* pHead, LinkListNode* ptr, ElemType x) {
	LinkListNode* s, *qPtr;
	s = (LinkListNode*)malloc(sizeof(LinkListNode));
	s->data = x;
	qPtr = pHead;//qPtr是用来代替pHead的移动的,ptr是目标节点
	while (qPtr->pNext != ptr)
		qPtr = qPtr->pNext;
	s->pNext = ptr;
	qPtr->pNext = s;
	//因为链表是单向的,虽然我知道当前节点是ptr
	//但是在语法层面上,我如果想知道ptr的前继节点只能从head遍历而来
	//查到了当前节点的前继,才能使用后插的方法完成节点的加入
}


//删除:删除指定位置的值或者是删除给定的值。
//情形一:删除指定节点的后继结点
//情形二:删除第i个节点,假定头节点i=0
//删除返回目标节点的地址,并不涉及到动态空间的回收
//在动态回收空间的要求中,应该遵循的原理是谁污染谁治理
//在顺序表中的删除就是逻辑上的删除,就是说我们的这个节点不再
//存在于当前的顺序表中了
///删除节点:Ptr是需要删除的节点,将删除ptr的后续节点
LinkListNode* Delete_After_LkList(LinkListNode* ptr) {
	LinkListNode* fptr;
	//假定我们的顺序表A-》B-》C,我们要删除的是A的后续节点B,A-》C
	fptr = ptr->pNext;// ptr是A,所以ptr的next是B,所以fptr是B
	ptr->pNext = fptr->pNext;//ptr是A,fptr是B,所以fptr的next是C,所以ptr-next就变成C
	return fptr;
}

///删除第i个节点
LinkListNode* Delete_i_LkList(LinkListNode* pHead, int i) {
	LinkListNode*ptr, *qPtr = NULL;
	ptr = GetLinkListNode(pHead, i - 1);//找到i的前继节点
	if (ptr != NULL && ptr->pNext != NULL)
		qPtr = Delete_After_LkList(ptr);
	return qPtr;
}
//遍历
void ShowLkList(LinkListNode* pHead) {
	LinkListNode* p = pHead->pNext;
	while (p != NULL) {
		printf(" %d", p->data);
		p = p->pNext;
	}
}

int main3(void) {
	ElemType MySeq[] = { 1,2,3,4,5 };
	LinkListNode* pHead = Create_Rear_LkList(MySeq, 5);
	printf("\n********显示当前单链表中的全部元素******\n");
	ShowLkList(pHead);
	LinkListNode* pPos = GetLinkListNode(pHead, 2);
	Insert_After_LkList(pPos, 999);
	printf("\n********显示当前单链表中的全部元素******\n");
	ShowLkList(pHead);
	Insert_Before_LkList(pHead, pPos, 666);
	printf("\n********显示当前单链表中的全部元素******\n");
	ShowLkList(pHead);
	//Delete_After_LkList(pPos);
	Delete_i_LkList(pHead, 2);
	printf("\n********显示当前单链表中的全部元素******\n");
	ShowLkList(pHead);
	printf("\nList Size:%d", GetSizeLinkList(pHead));
	getchar();
	return 0;
}

顺序表转置




设有一个顺序表,其数据为a,b,c,d,e,f,g,要求将其就地转置成g,f,e,d,c,b,a
要求逆转表仍然占据原空间
//#include "SeqList.h"
//SequenList* pGlobalSeq;
//
//void RevseSeqList(SequenList* pList) {
//	int temp;
//	int count, i;
//	if (pList->last == 0 || pList->last == 1)
//		return;
//	count = pList->last / 2;
//	for (i = 0;i < count;i++) {
//		temp = pList->data[i];
//		pList->data[i] = pList->data[pList->last - 1 - i];
//		pList->data[pList->last - 1 - i] = temp;
//	}
//}
//int main(void) {
//	pGlobalSeq=InitSeq();
//	for (int i = 0;i < 10;i++) {
//		InsertElemSqList(pGlobalSeq, i * 2, i);
//	}
//	printf("\n**********当前的顺序表元素********\n");
//	showSeqList(pGlobalSeq);
//	RevseSeqList(pGlobalSeq);
//	printf("\n**********当前的顺序表元素********\n");
//	showSeqList(pGlobalSeq);
//	free(pGlobalSeq);
//	getchar();
//	return 0;
//}
//Ctrl+a Ctrl+k Ctrl+C

#include "LinkList.h"

LinkListNode* ReverseLkList(LinkListNode* pHead) {
	LinkListNode* Pointer, *Next;//Pointer是将来依次获得的当前节点
	//Next:将来获得的当前节点的原先的下一个节点
	LinkListNode* Back; //原先节点的上一个节点
	//step 1: 将原先的next节点的next域变为pointer
	//step 2:将原先的pointer的next域变成back

	Back = pHead; //原先就是从头节点开始,因此,我们首先将back作为头节点
	Pointer = Back->pNext;//通过头节点获得了第一个元素=>pointer指向第一个元素

	Next = Pointer->pNext;//此时pointer是第一个元素,因此它的pNext是第二个元素
	Pointer->pNext = Back;//pointer->pNext域变成了Back,又因为Back是头节点,所以
	//此时原先的第一个元素的后继已经变成了原先的头节点
	Back = Pointer;//pointer是第一个元素,因此,此时Back变成了第一个元素
	Pointer = Next;//因为在46行,Next已经是第二个元素了,
	//因此,此时pointer指向了第二个元素

	//在46行,Next变成了原先的第二个元素,有因为在46行-51行,没有在对Next的pNext域
	//进行操作,因此,原先链表中的后继信息没有破坏
	//47行,pointer作为当前元素指向了原先的前继节点的时候,此时,当前元素把原先的前继元素
	//变成了后续节点
	//48行,再将Back设为当前元素,49行将Pointer设为下一个元素

	while (Pointer->pNext != NULL) {
		Next = Pointer->pNext;
		Pointer->pNext = Back;
		Back = Pointer;
		Pointer = Next;
	}

	Pointer->pNext = Back;
	//此时Pointer已经是原先链表中的最后一个元素了,
	//因此,必须要把Pointer和原先的前继节点联系起来,而原先的前继节点一定在Back

	//此时反转完毕,我们需要重新设置头节点
	//原先的pHead没有变,因此原先的pHead->pNext指向的原先的第一个元素
	//但是现在我们要让这个元素的pNext域置为空
	pHead->pNext->pNext = NULL;
	//由于pHead没有变,所以我们依然让它作为反转后链表的头节点
	//所以将当前的首元素放置它的pnext域中
	pHead->pNext = Pointer;

	return pHead;



}
int mainAAA(void) {
	LinkListNode* pHead = NULL;
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	pHead = Create_Rear_LkList(arr, 10);
	printf("\n************当前的元素有\n");
	ShowLkList(pHead);
	
	pHead = ReverseLkList(pHead);
	printf("\n************当前的元素有\n");
	ShowLkList(pHead);
	getchar();
	return 0;
}

双向链表



#include 
#include 

typedef struct DNode {
	int data;
	struct DNode* prior, *next;
}DLinkListNode;


DLinkListNode* Delete_DL(DLinkListNode* ptr) {
	ptr->prior->next = ptr->next;
	ptr->next->prior = ptr->prior;//在逻辑上,我们再也无法找到这个ptr
	return ptr;
}

void Insert_Before_DL(DLinkListNode* p, int x) {
	DLinkListNode* s;
	s = (DLinkListNode*)malloc(sizeof(DLinkListNode));
	s->data = x;
	//将s的关系加入到链表
	s->prior = p->prior;
	if (p->prior != NULL)
		p->prior->next = s;
	s->next = p;
	p->prior = s;

}

int main(void) {
	
	DLinkListNode x;
	DLinkListNode *head;
	DLinkListNode *ptr;
	ptr = &x;
	ptr->data = 3;
	ptr->prior = NULL;
	ptr->next = NULL;

	Insert_Before_DL(ptr, 1);
	Insert_Before_DL(ptr, 2);
	DLinkListNode* p,*q;
	p = &x;
	while (p != NULL) {
		printf("%d\n", p->data);
		if (p->prior == NULL)
			q = p;//q记录了第一个节点
		p = p->prior;
	}
	
	while (q != NULL) {
		printf("%d\n", q->data);
		q = q->next;
	}
	getchar();
	return 0;
}

循环链表的建立



#include 
#include 

///循环链表的建立
///输入:顺序表集合,顺序表长度
///输出:循环链表的尾地址
//定义节点
typedef struct node {
	int data;
	struct node *pNext;
}LinkCircleListNode;

LinkCircleListNode* Create_Circle_LkList(int arr[], int length) {
	LinkCircleListNode *head, *p, *q;
	int i;
	head = (LinkCircleListNode*)malloc(sizeof(LinkCircleListNode));
	q = head;

	for (i = 0;i < length;i++) {
		//p指向了临时申请的空间
		p = (LinkCircleListNode*)malloc(sizeof(LinkCircleListNode));
		p->data = arr[i];
		q->pNext = p;//q在串联p的节点
		q = p;//q移动到当前的节点
	}
	p->pNext = head;
	return p;
}

//遍历输出
void ShowCircleLkList(LinkCircleListNode* pRear) {
	LinkCircleListNode* p = pRear->pNext;//循环链表建立的尾指针的next域指向的是头节点
										  //因为遍历的时候,我们希望获得的真正元素,所以我们首先让p移动
	p = p->pNext;//此时p指向第一个真正的元素
				 //while (p->pNext != pRrear->pNext) {//当且仅当p == pRear的时候,循环结束,那么最后一个reare节点是没有输出的
	while (p != pRear->pNext) {
		printf("%d\n", p->data);
		p = p->pNext;
	}
}


//两个循环链表合并
//输入为链表a的尾指针,链表b的尾指针
LinkCircleListNode * Connect_Circle_LkList(LinkCircleListNode* ra, LinkCircleListNode *rb) {
	LinkCircleListNode* p;
	//记录a的头节点
	p = ra->pNext;
	//将b的第一个元素节点链入a的尾部
	ra->pNext = rb->pNext->pNext;
	free(rb->pNext);//回收空间,意味着b链表的头节点空间不可达,但是rb->pNext依然是有值的
	rb->pNext = p;
	//假定rb0x20空间,在ox20空间下有一个next的分量,这个分量是可以存储地址值
	//free(rb)才是是的rb->pnext不可用
	//free(rb->pnext),rb->pnext中的(data和pnext)分量不可用
	//rb->pNext始终指向的是原b链表的头节点
	//所以52行执行完毕以后,将b链表的尾指针指向了a链表的头节点

	return rb;
}

int mainCCCC(void) {
	int arr[] = { 11,22,33,44,55 };
	LinkCircleListNode* pRrear = Create_Circle_LkList(arr, 5);
	//

	//测试合并
	int a[] = { 1,3,5 };
	int b[] = { 2,4,6 };
	LinkCircleListNode *ra, *rb;
	ra = Create_Circle_LkList(a, 3);
	rb = Create_Circle_LkList(b, 3);
	LinkCircleListNode * pRear =Connect_Circle_LkList(ra, rb);
	ShowCircleLkList(pRear);
	getchar();
	return 0;
}

约瑟夫环



#include 
#include 

typedef struct node {
	int data;
	struct node* pNext;
}LinkJoeNode;

//考虑建立约瑟夫环的循环链表
//不带头节点:需要模拟约瑟夫环的工作过程
LinkJoeNode* CreateLkList(int arr[], int length) {
	LinkJoeNode *head, *p, *q;
	int i;
	head = (LinkJoeNode*)malloc(sizeof(LinkJoeNode));
	head->data = arr[0];
	q = head;

	for (i = 1;i < length;i++) {
		p = (LinkJoeNode*)malloc(sizeof(LinkJoeNode));
		p->data = arr[i];
		q->pNext = p;
		q = p;
	}

	p->pNext = head;
	return p;

}


//是不是要写show方法:打印输出大法,log


int mainDDDD(void) {

	//从位置1开始,初始30个人,剩余15个人
	int s = 1;
	int m = 9;//每逢9就出圈
	int k = 15;//表明出圈者要有15个人
	
	int arr[30];
	//构造初始的序列
	for (int i = 0;i < 30;i++) {
		arr[i] = i + 1;//因为我们为每一个变量的序号设为i+1
	}

	LinkJoeNode *p, *q;
	LinkJoeNode *rear = CreateLkList(arr, 30);
	p = rear;//p是遍历指针,只要游戏运行,那么这个p就要移动

	while (k > 0) {

		//step1:只要人数没有达到15个人,那么就要找出第m-1个,因为它的后继是即将要出圈 
		for (int j = 0;j < m - 1;j++) {
			p = p->pNext;
		}

		//此时,p停在了m-1处
		q = p->pNext;//q指向了第m个元素
		printf("第%d个元素出圈\n", q->data);
		//让m-1和m+1相连,此时q指向的第m个元素依然存在在内存空间当中,只是逻辑上
		//不再存在和m元素相关联的联系
		p->pNext = q->pNext;//p->pNext->pNext;
		//为了防止内存泄漏
		free(q);
		k--;
	}

	getchar();
	return 0;
}

一元多次多项式的加减



#include "stdio.h"
#include "stdlib.h"

//设计数据项的数据结构
typedef struct Polynode {
	int coef;//系数
	int exp;//指数
	Polynode* pNext;
}LinkPolyNode;

//面临算法抽象:补充说明:所有的存储结构按照幂指数升序


//我们需要新增节点,因为有可能在原链表不存在
///新多项式元素的地址
///coef:系数,exp:幂指数,pc:新节点要插入的位置
///返回值:新分配的节点地址
LinkPolyNode* attach(int coef, int exp, LinkPolyNode* pc) {
	LinkPolyNode* p;
	p = (LinkPolyNode*)malloc(sizeof(LinkPolyNode));
	p->coef = coef;
	p->exp=exp;
	pc->pNext = p;
	return p;
}


///将PolyA和PolyB进行相加,将结果放入PolyC中
///函数的输入是PolyA和PolyB的首地址
///函数返回的是PolyC的首地址
LinkPolyNode* MergePoly(LinkPolyNode* headA, LinkPolyNode* headB) {
	LinkPolyNode* headC;//需要返回的多项式C的地址
	LinkPolyNode *pa, *pb, *pc, *p;

	int x;
	pa = headA->pNext;
	pb = headB->pNext;
	headC = (LinkPolyNode*)malloc(sizeof(LinkPolyNode));
	pc = headC;//此时将pc做C多项式的头节点

	while (pa && pb) {
		if (pa->exp == pb->exp) {
			x = pa->coef + pb->coef;
			if (x != 0)
				pc = attach(x, pa->exp, pc);
			pa = pa->pNext;
			pb = pb->pNext;
			continue;
		}
		if (pa->exp < pb->exp) {
			p = pa;pa = pa->pNext;
		}
		else {
			p = pb;pb = pb->pNext;
		}
		pc = attach(p->coef, p->exp, pc);
	}
	p = pa;
	if (pa == NULL) p = pb;
	while (p) {
		pc = attach(p->coef, p->exp, pc);
		p = p->pNext;
	}
	pc->pNext = NULL;
	return headC;


}

//假定多项式的最低项是常数项,假定所有的元素都是关于X的多项式
//PolyA: 1+2x+3x^2
//PolyB: 3x+x^4


///设计显示的辅助函数
void ShowList(LinkPolyNode* pHead) {
	LinkPolyNode* p;
	p = pHead->pNext;
	while (p != NULL) {

		//美化设计显示的内容:设计常数项的显示
		if (p->exp == 0) {
			printf("%d", p->coef);
			p = p->pNext;
			continue;
		}
		
		//设计一次项的显示
		if (p->exp == 1) {
			//printf("x")
			if (p->coef > 0) {
				printf("+%dx",p->coef);
			}
			else {
				printf("%dx", p->coef);
			}
			p = p->pNext;
			continue;
		}

		//设计高此项的显示
		if (p->coef > 0) {
			printf("+%dx^%d", p->coef, p->exp);
		}
		else {
			printf("%dx^%d", p->coef, p->exp);
		}

		p = p->pNext;
	}
}

/*程序是调出来的,不是一蹴而就的,只有掌握了原理才能完成*/

int mainBBB(void) {


	//PolyA: 1+2x+3x^2
	//PolyB: 3x+x^4

	LinkPolyNode HeadA,A1,A2,A3;
	LinkPolyNode HeadB, B1, B2;

	HeadA.pNext = &A1;
	A1.pNext = &A2;
	A2.pNext = &A3;
	A3.pNext = NULL;

	HeadB.pNext = &B1;
	B1.pNext = &B2;
	B2.pNext = NULL;

	//建立多项式:PolyA: 1+2x+3x^2
	A1.coef = 1;
	A1.exp = 0;
	A2.coef = 2;
	A2.exp = 1;
	A3.coef = 3;
	A3.exp = 2;

	//PolyB: 3x+x^4
	B1.coef = 3;
	B1.exp = 1;
	B2.coef = 1;
	B2.exp = 4;

	printf("\n*********PolyA********\n");
	ShowList(&HeadA);
	printf("\n*********PolyB********\n");
	ShowList(&HeadB);

	LinkPolyNode* pPloyC;
	pPloyC = MergePoly(&HeadA, &HeadB);
	printf("\n*********PolyC********\n");
	ShowList(pPloyC);



	getchar();
	return 0;
}

学生成绩管理系统



#include 
#include 
#include 
using namespace std;
typedef struct student {
	int num;
	float score;
}node;

//单班人数上限是50
#define LIST_SIZE 50

#define TRUE 1
#define FALSE 0

//定义顺序表的结构
typedef struct {
	node data[LIST_SIZE];
	int last;//表明最后一个节点的位置
}SeqList;

//获取班级人数
int GetSize(SeqList* pList) {
	return pList->last;
}

//遍历输出全体学生信息
void ShowSeqList(SeqList* pList) {
	for (int i = 0;i < pList->last;i++) {
		printf("学生的学号是%d,成绩是%f\n", pList->data[i].num, pList->data[i].score);
	}
}

//初始化
SeqList* InitSeq() {
	SeqList* pList = NULL;
	pList = (SeqList*)malloc(sizeof(SeqList));
	if (pList != NULL)
		pList->last = 0;//初始化成功,且长度为0
	return pList;
	//因为在服务器分配中,连续分配大量空间,往往不容易成功
	//所以我们经常在内部就进行判断
}

//插入元素
int InesrtStuSeq(SeqList* pList, node *pNode, int k) {
	int j;
	/*if (pList->last >= LIST_SIZE - 1)
		return FALSE;*/
	//移到函数之外,有业务人员维护插入
	//如果这个函数被反复调用的开销很大,那么我们会放弃内部校验
	//但是,如果这是一个对安全要求很高程序,内部校验依然需要
	//如果去掉了内部校验,在实际的公司开发中,会有Team leader做codereview
	
	//在本例中,我们假定k传入就是有效位置。
	//永远不要相信调用者传入的数据,但是,在具体开发中,还是具体情况具体分析
	for (j = pList->last;j >= k;j--) {
		pList->data[j + 1].num = pList->data[j].num;
		pList->data[j + 1].score = pList->data[j].score;

	}
	pList->data[k].num = pNode->num;
	pList->data[k].score = pNode->score;
	pList->last++;
	return TRUE;
}

int DelSeqList(SeqList* pList, int k) {
	/*if (k <= 0 || k > pList->last)
		return FALSE;*/

	if (pList->last == 0)
		return FALSE;

	for (int j = k;j <= pList->last;j++) {
		pList->data[j].num = pList->data[j + 1].num;
		pList->data[j].score = pList->data[j + 1].score;
	}
	pList->last--;
	return TRUE;
}


//实现按学号查找的
void DisplayStu(SeqList* pList, int stuNo) {
	for (int i = 0;i < pList->last;i++) {
		if (pList->data[i].num == stuNo) {
			printf("该学生的成绩是%f\n", pList->data[i].score);
			return;
		}
	}
	printf("该学生不存在\n");
}
int main222(void) {
	SeqList* stuList = InitSeq();
	node stu[2];
	stu[0].num = 1;
	stu[0].score = 99;
	stu[1].num = 2;
	stu[1].score = 100;
	if (stuList) {
		for (int i = 0;i < 2;i++) {
			InesrtStuSeq(stuList, &stu[i], i);
		}

	}
	ShowSeqList(stuList);
	//DelSeqList(stuList, 1);
	//ShowSeqList(stuList);
	printf("\n************\n");
	DisplayStu(stuList, 1);
	getchar();
	return 0;
}

************************************************************************************************************************************************

SeqStack.h
 



#pragma once
//假定处理的是一位数,没有括号
#define stackSize 100

#define TRUE 1
#define FALSE 0

typedef int BOOL;
typedef int ElemType;//元素类型

typedef struct {
	ElemType stack[stackSize];
	int top;//栈顶指针
}SeqStack;



///栈的初始化
BOOL init(SeqStack* pStack);

///栈的判空
BOOL isEmpty(SeqStack* pStack);

///压栈
///返回值为1表示成功,-1表示错误,
///pStack表示顺序栈,x表示压入元素
BOOL pushStack(SeqStack* pStack, ElemType x);

///出栈
ElemType popStack(SeqStack* pStack);



///获取栈顶元素
///如果栈为空,返回取值不成功,否则直接取出栈顶元素供用户使用
BOOL GetSeqStack(SeqStack* s, ElemType* data);

//判断是否是运算符,如果是运算符,返回true,否则返回false
BOOL Is_Operator(char oper);
//判断优先级:×/ 为2,+,-为1,
int Priority(char oper);

//计算数值:不考虑浮点数,也不考虑除数为0
int two_res(int oper, int opand1, int opand2);

SeqStack

 


#include 

#define stackSize 100

#define TRUE 1
#define FALSE 0

typedef int BOOL;
typedef int ElemType;//元素类型

typedef struct {
	ElemType stack[stackSize];
	int top;//栈顶指针
}SeqStack;



///栈的初始化
BOOL init(SeqStack* pStack) {
	pStack->top = 0;
	return 1;
}

///栈的判空
BOOL isEmpty(SeqStack* pStack) {
	return (pStack->top == 0);
}

///压栈
///返回值为1表示成功,-1表示错误,
///pStack表示顺序栈,x表示压入元素
BOOL pushStack(SeqStack* pStack, ElemType x) {
	if (pStack->top == stackSize)
	{
		printf("空间溢出\n");
		return FALSE;
	}
	pStack->top = pStack->top + 1;
	pStack->stack[pStack->top] = x;
	return TRUE;
}

///出栈
ElemType popStack(SeqStack* pStack) {
	ElemType tmp;
	if (pStack->top == 0) {
		printf("当前是空栈\n");
		return -1;
	}
	tmp = pStack->stack[pStack->top];
	pStack->top = pStack->top - 1;//逻辑上的出栈
	return tmp;
}

void myConv(SeqStack* pStack, int n) {
	int e;//每一个元素
	while (n) {
		pushStack(pStack, n % 8);
		n = n / 8;
	}

	while (!isEmpty(pStack)) {
		e = popStack(pStack);
		printf("%d", e);
	}
}

///获取栈顶元素
///如果栈为空,返回取值不成功,否则直接取出栈顶元素供用户使用
BOOL GetSeqStack(SeqStack* s, ElemType* data) {
	if (s->top == 0)
		return FALSE;
	*data = s->stack[s->top];
	return TRUE;
}


int main1(void) {

	SeqStack s;
	init(&s);
	printf("十进制数1348,转为的8进制是:\n");
	myConv(&s, 1348);
	getchar();
	return 0;
}

/*
首先我们必须知道,一旦设立了栈,那么就请按照栈的特点进行操作
“先进后出”
我们都知道,只要知道下标就可以获取数组的元素
stack[0],stack[1],stack[2].....
我们没有能力再直接操作上面的元素
我们只能取栈顶,因为只有这一个函数;其他,要么出栈,要么进栈
这就是数据结构思考问题方式:我们设立一个逻辑结构,就用这个结构控制
程序逻辑
*/

LinkStack


#include 
#include 

typedef int datatype;
typedef struct node {
	datatype data;
	struct node * pNext;
	
}LinkStack;

//入栈
LinkStack* PushLkStack(LinkStack* top, datatype x) {
	LinkStack* p;
	p = (LinkStack*)malloc(sizeof(LinkStack));
	p->data = x;
	p->pNext = top; //类似于首先将p链入链表
	top = p;//top上移到当前栈顶,指向刚刚加入的p节点,此时top永远指向栈顶的第一个元素
	return top;
}


//出栈
LinkStack*  PopLkStack(LinkStack* top, datatype* pData) {
	LinkStack* p;
	//一旦我们出栈,那么我们认为这个元素就不应当维护链表指向的关系了
	if (top != NULL) {
		*pData = top->data;
		p = top;//此时我们将当前的栈顶元素的位置,保存下来了,接下栈顶元素即将脱离
		top = p->pNext;
		free(p);
	}
	return top;

}



int main2(void) {
	LinkStack* pTop=NULL;
	pTop = PushLkStack(pTop, 1);
	pTop = PushLkStack(pTop, 1);
	getchar();
	return 0;
}

SeqQueue



#include 
#include 

#define QUEUE_SIZE 100
typedef int datatype;

#define TRUE 1
#define FALSE 0

typedef int BOOL;
typedef struct queue {
	datatype data[QUEUE_SIZE];
	int front, rear;
}SeqQueue;

void Init_SqQueue(SeqQueue* sq) {
	sq->front = QUEUE_SIZE - 1;
	sq->rear = QUEUE_SIZE - 1;
}

BOOL IsEmpty_SqQueue(SeqQueue *sq) {
	if (sq->rear == sq->front)
		return TRUE;
	return FALSE;
}

//返回的应当是数组的角标
int Get_SqQueue(SeqQueue* sq) {
	if (!IsEmpty_SqQueue(sq)) {
		return (sq->front + 1) % QUEUE_SIZE;
	}
	return -1;
}

BOOL EnSqQueue(SeqQueue* sq, datatype x) {
	if (sq->front == (sq->rear + 1) % QUEUE_SIZE)
		return FALSE;
	sq->rear = (sq->rear + 1) % QUEUE_SIZE;//队尾指针后移
	sq->data[sq->rear] = x;//元素入队
	return TRUE;
}

///出队如果队空,返回的是元素的角标记为-1;否则,返回数组角标
int DeSqQueue(SeqQueue* sq) {
	if (!IsEmpty_SqQueue(sq)) {
		sq->front = (sq->front + 1) % QUEUE_SIZE;
		return sq->front;
	}
	return -1;
}

int  mainAA(void) {
	return 0;
}

LkQueue

 



#include 
#include 

typedef int BOOL;
#define TRUE 1
#define FALSE 0
typedef int datatype;
typedef struct node {
	datatype data;
	struct node * next;	
}LinkListNode;

typedef struct {
	LinkListNode* front, *rear;
}LinkQueue;

void Init_LkQueue(LinkQueue* lq) {
	lq->front = (LinkListNode*)malloc(sizeof(LinkListNode));
	lq->front->next = NULL;
	lq->rear = lq->front;
}

BOOL IsEmpty_LkQueue(LinkQueue* lq) {
	if (lq->front == lq->rear)
		return TRUE;
	else
		return FALSE;
}

BOOL Get_LkQueue(LinkQueue* lq, datatype *x) {
	if (IsEmpty_LkQueue(lq))
		return FALSE;
	x = &(lq->front->next->data);//因为front始终指向头节点,而队列是先进先出,front的next一定是队头元素 
	return TRUE;
}

void Insert_LkQueue(LinkQueue* lq, datatype x) {
	lq->rear->next = (LinkListNode*)malloc(sizeof(LinkListNode));
	//新节点入队尾部
	lq->rear = lq->rear->next;
	lq->rear->data = x;
	lq->rear->next = NULL;
}

LinkListNode* Del_LkQueue(LinkQueue* lq) {
	LinkListNode* s;
	if (!IsEmpty_LkQueue(lq)) {
		s = lq->front->next;
		if (s->next == NULL)//队中只有一个节点
			lq->rear = lq->front;//队列置空
		else
			lq->front->next = s->next;//因为lq是我们自己设计记录位置的指针,所以要让指向下一个队头元素
		return s;
	}
	return NULL;
}

void Destroy_LkQueue(LinkQueue* lq) {
	//即使销毁队列,也要保持住结构
	LinkListNode* s;
	while (!IsEmpty_LkQueue(lq)) {
		s = Del_LkQueue(lq);
		free(s);
	}
	free(lq->front);//释放头节点
	lq->front = NULL;
	lq->front = NULL;
}


int main222(void) {
	LinkQueue node;
	LinkQueue *lpPtr = &node;
	LinkListNode* sPtr;
	datatype value;
	Init_LkQueue(lpPtr);
	//0,1,1,0入队
	Insert_LkQueue(lpPtr, 0);
	Insert_LkQueue(lpPtr, 1);
	Insert_LkQueue(lpPtr, 1);
	Insert_LkQueue(lpPtr, 0);
	int N = 42;

	for (int i = 0;i < 42;i++) {
		//队头元素出队
		sPtr = Del_LkQueue(lpPtr);
		if(sPtr!=NULL){
			//队头的两个元素,n-1 n 生成新的系数
			value = sPtr->data + lpPtr->front->next->data;
			if (sPtr->data != 0) {
				printf("%d ", sPtr->data);
			}
			Insert_LkQueue(lpPtr, value);
		}
		if (lpPtr->front->next->data == 0) {
			//队头元素作为分隔符
			Insert_LkQueue(lpPtr, 0);
			printf("\n");
		}
	}

	getchar();
	return 0;
}

八皇后问题

 



#include 
#define TRUE 1
#define FALSE 0

int a[9], b[17], c[17],s[9];//用来处理第i行上第j个元素的冲突问题
void eightQueen() {
	int i, j;
	//因为这样对角线有15条,且i+j从2开始
	//初始化好全部的冲突,现在都可以放皇后
	for (i = 2;i <= 16;i++) {
		if (i >= 2 && i <= 9) {
			a[i - 1] = TRUE;
		}
		b[i] = TRUE;
		c[i] = TRUE;
	}

	i = 1;
	j = 1;
	//开始使用回溯法进行处理
	while (i<=8) {
		while (j <= 8) {
			//在第i行寻找潜在的第j列
			if (a[j] && b[i + j] && c[i - j + 9])
				break;
			j++;
		}
		if (j <= 8) {
			//找到安全位置
			a[j] = FALSE;
			b[i + j] = FALSE;
			c[i-  j + 9] = FALSE;
			s[i] = j;
			i++;//前进到下一行
			j = 1;
		}
		else {
			//当前的第i行已经不能再放入元素了,那么回溯到上一行再寻找
			i--;
			j = s[i];
			a[j] = TRUE;
			b[i + j] = TRUE;
			c[i - j + 9] = TRUE;
			j++;//前进到下一列

		}

	}
}

int main333333() {
	eightQueen();
	for (int i = 1;i < 9;i++) {
		printf("%d,%d\n", i, s[i]);
	}
	getchar();
	return 0;
}

八皇后递归解法



#include 
//使用回溯,每一行放一个,直到不冲突
int c[20], n = 8, cnt = 0;

//找到一个解就是输出,c[]里面存的是可以放的皇后的列
void print() {
	for (int i = 0;i < n;++i) {
		for (int j = 0;j < n;++j) {
			if (j == c[i])
				printf("1 "); //1表示皇后占位
			else
				printf("0 "); //0表示空的位置
		}
		printf("\n");
	}
	printf("\n");
}

//用r表示搜索到了第r行
void search(int r) {
	if (r == n) {
		//说明已经放置成功
		print();
		++cnt;
		return;
	}

	//什么时候才能进入下一级递归呢?
	//只有满足了当前皇后和前面所有的皇后都不互相攻击的时候,才能进入下一级递归
	for (int i = 0;i < n;++i) {
		c[r] = i;
		int ok = 1;
		for (int j = 0;j < r;++j) {
			if (c[r] == c[j] || r - j == c[r] - c[j] || r - j == c[j] - c[r]) {
				ok = 0;
				break;
			}
		}
		if (ok)
			search(r + 1);
	}
}

int mainQQQ(void) {
	search(0);
	printf("八皇后一共有%d种解法", cnt);
	getchar();
	return 0;
}

八皇后问题的全解



#include 
#define TRUE 1
#define FALSE 0

int a[9], b[17], c[17], s[9];//用来处理第i行上第j个元素的冲突问题
void print() {
	printf("\n");
	for (int i = 1;i < 9;i++) {
		printf("%d,%d\n", i, s[i]);
	}

}
void  move(int i,int j) {
	a[j] = TRUE;
	b[i + j] = TRUE;
	c[i - j + 9] = TRUE;
}
void eightQueenEx() {
	int i, j;
	//因为这样对角线有15条,且i+j从2开始
	//初始化好全部的冲突,现在都可以放皇后
	for (i = 2;i <= 16;i++) {
		if (i >= 2 && i <= 9) {
			a[i - 1] = TRUE;
		}
		b[i] = TRUE;
		c[i] = TRUE;
	}

	i = 1;
	j = 1;
	//开始使用回溯法进行处理
	while (i <= 8) {
		while (j <= 8) {
			//在第i行寻找潜在的第j列
			if (a[j] && b[i + j] && c[i - j + 9])
				break;
			j++;
		}
		if (j <= 8) {
			//找到安全位置
			a[j] = FALSE;
			b[i + j] = FALSE;
			c[i - j + 9] = FALSE;
			s[i] = j;
			if (i == 8) {
				//找到一个解
				print();
				//移除i,j位置上的皇后
				move(i, j);
				//回溯退栈
				i = i - 1;
				j = s[i];
				move(i, j);
				j++;//从上一个的第j+1列再寻找
			}
			else{
			i++;//前进到下一行
			j = 1;
			}
		}
		else {
			//当前的第i行已经不能再放入元素了,那么回溯到上一行再寻找
			i--;
			if (i >= 1) {
				//如果栈不空,那么移除皇后 
				j = s[i];
				move(i, j);
				j++;//前进到下一列
			}
			
			
			

		}

	}
}

int main4444() {
	eightQueenEx();
	
	getchar();
	return 0;
}

/*
求的一个解,我们就打印,但是,不停止,
我们让我们的皇后移除,返回到上一行的下一个j列,看看能不能找到
如果上一行的j列全部遍历结束,那么再回溯到上上一行,直到到第一行为止
*/

递归的原理



#include 

int fun(int n) {
	if (n > 1)
		return n*fun(n - 1);
	if (n == 1)
		return 1;
}
int funG(int n) {
	int temp = 1;
	for (int i = 1;i <= n;i++) {
		temp *= i;
	}
	return temp;
}

int main55555(void) {
	printf("5*4*3*2=%d", funG(5));
	getchar();
	return 0;
}

函数栈帧调试剖析



#include 
//1+2+3+4+5....
//递归是我们程序设计入门一个槛
//我们可以使用重复的力量,来解决这个问题
int sum = 0;
int FunGaosi(int value) {
	if (value == 1)
		return 1;
	sum = value + FunGaosi(value - 1);
	return sum;
}

/*int go() {
	
}
int gogo() {
	
	go();
}

int fun() {
	gogo();
}*/
int main3(void) {
	printf("%d", FunGaosi(4));
	getchar();
	return 0;
}
//call stack最基本的作用:
/*
就是告诉程序员,你现在停留的指令在什么位置,当你的函数返回时
你将在哪一个函数的指令继续推进你的程序。
只有我们清楚的知道你的程序代码是如何推进,你才知道你的程序怎么运行的。
bug,是程序没有按照我们预定的逻辑进行推进,那么我们就必须知道
在哪里运行的路径不正确。
*/

后缀表达式_逆波兰表达式运算



#include 
#include "SeqStack.h"
int main222(void) {
	char expression[] = "1562/-3*+#";
	int position = 0;
	int operand1 = 0;
	int operand2 = 0;
	int evaluate = 0;//没有所谓的操作符

	SeqStack sStack;
	init(&sStack);
	while (expression[position]!='#')
	{
		if (Is_Operator(expression[position])) {
			operand1 = popStack(&sStack);
			operand2 = popStack(&sStack);
			pushStack(&sStack, two_res(expression[position], operand1, operand2));
		}
		else {
			pushStack(&sStack, expression[position] - 48);
		}

		position++;
	}

	evaluate = popStack(&sStack);
	printf("%d ", evaluate);

	getchar();
	return 0;
}

迷宫的递归解法



#include 
typedef int BOOL;
#define TRUE 1
#define FALSE 0
#define ROWS 7
#define COLUMNS 10

int MyMaze[][11] = 
{ { 1,1,1,1,1,1,1,1,1,1,1 },
{ 1,0,1,0,0,1,1,1,0,0,1 },
{ 1,0,0,0,0,0,1,0,0,1,1 },
{ 1,0,1,1,1,0,0,0,1,1,1 },
{ 1,0,0,0,1,0,1,1,0,1,1 },
{ 1,1,0,0,1,0,1,0,0,0,1 },
{ 1,1,1,0,0,0,0,0,1,0,1 },
{ 1,1,1,1,1,1,1,1,1,1,1 } };

//判断是否是障碍
BOOL Valid(int row, int col) {
	BOOL flag = FALSE;
	if (row >= 1 && row < ROWS && col >= 1 && col < COLUMNS && MyMaze[row][col] == 0)
		flag = TRUE;
	return flag;
}


//只要我们相信我们设计的递归函数,严格按照我们设计的指令序列
//依次,运行,那么这个函数本身就是蕴含答案的
//函数的栈帧重复运行将我们设计的如for,while循环的过程隐藏
//所以,直觉上会觉得递归抽象。
BOOL exitMaze(int row, int col) {
	BOOL done = FALSE;
	if (Valid(row, col)) {
		//走过的标记,我们给2
		MyMaze[row][col] = 2;

		//抵达出口
		if (row == (ROWS - 1) && col == (COLUMNS - 1)) {
			done = TRUE;
		}
		else {
			//未达到终点,我们就按照E-》S—》W—》N
			done = exitMaze(row, col + 1);
			if (!done)
				done = exitMaze(row + 1, col);
			if (!done)
				done = exitMaze(row, col - 1);
			if (!done)
				done = exitMaze(row - 1,col);
		}
		// 设一个可以找到的表示,我们将数字9表示可以通过的路径
		if (done)
			MyMaze[row][col] = 9;
	}
	return done;
}

int main(void) {
	BOOL done = exitMaze(1, 1);
	if (done) {
		for (int i = 0;i < ROWS;i++) {
			for (int j = 0;j < COLUMNS;j++) {
				printf("%d", MyMaze[i][j]);
			}
			printf("\n");
		}
	}
	else {
		printf("没有找到路径");
	}
	getchar();
	return 0;
}

迷宫问题的非递归解法



#include 
#include 

typedef int BOOL;
#define TRUE 1
#define FALSE 0

typedef struct {
	int x; //表示节点的横坐标

	int y;//表示节点的纵坐标
	int d;//表示节点的方向
}DataType;

typedef struct linknode {
	DataType data;
	struct linknode* pNext;
}LinkStack;

LinkStack* InitLkStack(void) {
	LinkStack* p = NULL;
	p = (LinkStack*)malloc(sizeof(LinkStack));
	if (p != NULL)
		p->pNext = NULL;
	return p;
}//建立一个带头节点的链表作为链栈

BOOL IsEmptyLkMazeStack(LinkStack* top) {
	return (top->pNext == NULL);
}

LinkStack* PushLkStack(LinkStack* top, DataType elem) {
	LinkStack* p;
	p = (LinkStack*)malloc(sizeof(LinkStack));
	p->data.x = elem.x;
	p->data.y = elem.y;
	p->data.d = elem.d;
	p->pNext = top; //p节点尾插入lkStack
	top = p;//top栈顶指针上移
	return top;
}

//出栈
LinkStack* PopLkStack(LinkStack* top, DataType *pData) {
	LinkStack* p;
	if (top != NULL) {
		*pData = top->data;
		p = top;
		top = p->pNext;
		free(p);
	}
	return top;
}

int main1add(void) {
	//迷宫
	int maze[][11] = {
		{1,1,1,1,1,1,1,1,1,1,1 },
		{1,0,1,0,0,1,1,1,0,0,1 },
		{1,0,0,0,0,0,1,0,0,1,1 },
		{1,0,1,1,1,0,0,0,1,1,1 },
		{1,0,0,0,1,0,1,1,0,1,1 },
		{1,1,0,0,1,0,1,0,0,0,1 },
		{1,1,1,0,0,0,0,0,1,0,1 },
		{1,1,1,1,1,1,1,1,1,1,1 },
	};

	//路径移动辅助数组
	int direction[4][2] = {
		{0,1},
		{1,0},
		{0,-1},
		{-1,0}
	};

	LinkStack* pTop = NULL;
	pTop = InitLkStack();//用来保存路径

	DataType element;
	element.x = 1;
	element.y = 1;
	element.d = -1;//保证下一个节点推进是起点方向是0
	pTop = PushLkStack(pTop, element);//迷宫入口入栈
	int i, j, k;//当前的横坐标,纵坐标以及方向
	int g, h;//潜在的下一个方向
	while (!IsEmptyLkMazeStack(pTop)) {
		//获得当前节点的坐标
		i = pTop->data.x;
		j = pTop->data.y;
		k = pTop->data.d+1;
		while (k <= 3) {
			g = i + direction[k][0];
			h = j + direction[k][1];
			if (g == 6 && h == 9 && maze[g][h] == 0) {
				printf("找到了\n");
				printf("节点位置是:%d,%d\n", i, j);
				while (!IsEmptyLkMazeStack(pTop)) {
					pTop = PopLkStack(pTop, &element);
					printf("节点位置是:%d,%d\n", pTop->data.x, pTop->data.y);
				}
				getchar();
				return;
			}

			//判断是否可以前进到下一个节点
			if (maze[g][h] == 0) {
				maze[g][h] = 2;//不要走回头路
				//要将节点路径保留,那么就要入栈
				element.x = g;
				element.y = h;
				element.d = -1;
				pTop = PushLkStack(pTop, element);
				break;
			}
			k = k + 1;
		}
		//回溯:k大于3说明,所有的当前节点的4个方向都找过,没有通路,所以要退栈,当上一个节点上
		if (k > 3) {
			pTop = PopLkStack(pTop, &element);//此时element接住上一个在栈中的节点
		}
	}
	printf("没有找到通路\n");
	return 0;
}

使用队列设计实现杨辉三角形



//思想:看看我们需要设计的业务中,有那些可以用我们已经学过的数据结构
//进行刻画,将这些业务问题,变成数据结构的组织问题
#include 

int mainA(void) {
	int n = 50;
	int queue[42] = { 0,1,1,0 };//0是换行
	int front = 1,rear = 4;//首先将杨辉三角的第一行输入
	for (int i = 0;i < n;i++) {
		queue[rear] = queue[front - 1] + queue[front];
		//将队头的两个元素和插入rear位置
		printf("%d ", queue[rear]);
		rear++;//准备好新元素的位置
		if (queue[front] == 0) {
			//添加新的分割
			queue[rear] = 0;
			rear++;
			printf("\n");
		}
		front++;
	}
	getchar();
	return 0;
}
//front是即将要处理的数据,rear是添加的数据。

中缀表达式求值



#include 
#include 
#include "SeqStack.h"



///栈的初始化
BOOL init(SeqStack* pStack) {
	pStack->top = 0;
	return 1;
}

///栈的判空
BOOL isEmpty(SeqStack* pStack) {
	return (pStack->top == 0);
}

///压栈
///返回值为1表示成功,-1表示错误,
///pStack表示顺序栈,x表示压入元素
BOOL pushStack(SeqStack* pStack, ElemType x) {
	if (pStack->top == stackSize)
	{
		printf("空间溢出\n");
		return FALSE;
	}
	pStack->top = pStack->top + 1;
	pStack->stack[pStack->top] = x;
	return TRUE;
}

///出栈
ElemType popStack(SeqStack* pStack) {
	ElemType tmp;
	if (pStack->top == 0) {
		printf("当前是空栈\n");
		return -1;
	}
	tmp = pStack->stack[pStack->top];
	pStack->top = pStack->top - 1;//逻辑上的出栈
	return tmp;
}



///获取栈顶元素
///如果栈为空,返回取值不成功,否则直接取出栈顶元素供用户使用
BOOL GetSeqStack(SeqStack* s, ElemType* data) {
	if (s->top == 0)
		return FALSE;
	*data = s->stack[s->top];
	return TRUE;
}

//判断是否是运算符,如果是运算符,返回true,否则返回false
BOOL Is_Operator(char oper) {
	switch (oper)
	{
	case '+':
	case'-':
	case '*':
	case '/':
		return TRUE;
	default:
		return FALSE;
	}
}

//判断优先级:×/ 为2,+,-为1,
int Priority(char oper) {
	switch (oper)
	{
	case '+':
	case '-':
		return 1;
	case '*':
	case '/':
		return 2;
	default:
		return 0;
	}
}

//计算数值:不考虑浮点数,也不考虑除数为0
int two_res(int oper, int opand1, int opand2) {
	switch (oper)
	{
	case '+': return (opand2 + opand1);
	case '-':return (opand2 - opand1);
	case '*':return (opand2 * opand1);
	case '/':return (opand2 / opand1);
	default:
		return 0;
	}
}


int main1111(void) {
	char expression[] = "8*9-4/2";
	int position = 0;//扫描的字符串
	int op = 0;//运算符
	int operand1 = 0;
	int operand2 = 0;
	int evaluate = 0;//运算结果

	SeqStack sOperator;//操作符栈
	init(&sOperator);
	SeqStack sOperand;
	init(&sOperand);// 操作数栈

	//第一步扫描字符串
	while (expression[position] != '\0' && expression[position] != '\n') {
		if (Is_Operator(expression[position])) {
			if (!isEmpty(&sOperator)) {
				while (Priority(expression[position])<=
					Priority(sOperator.stack[sOperator.top])
					&&!isEmpty(&sOperator)) {
					operand1 = popStack(&sOperand);
					operand2 = popStack(&sOperand);
					op = popStack(&sOperator);
					pushStack(&sOperand, two_res(op, operand1, operand2));
				}
			}
			pushStack(&sOperator, expression[position]);


		}
		else {
			pushStack(&sOperand, expression[position] - 48);//将Ascii码转成数值

		}
		position++;
	}

	//清空操作符栈 
	while (!isEmpty(&sOperator)) {
		op = popStack(&sOperator);
		operand1 = popStack(&sOperand);
		operand2 = popStack(&sOperand);
		pushStack(&sOperand, two_res(op,operand1, operand2));
	}
	
	printf("%d", popStack(&sOperand));
	


	getchar();
	return 0;
}

中缀转后缀



#include 
#include "SeqStack.h"
//对扩展开放,对修改封闭

//修改一下运算符
int Is_OperatorEx(char op) {
	switch (op)
	{
	case '(':
	case ')':
	case '+':
	case '-':		
	case '*':
	case '/':
		return 1;
	default:
		return 0;
	}
}

int PriorityEx(char op) {
	switch (op)
	{
	case '(':return 1;
	case '+':
	case '-':return 2;
	case '*':
	case '/':
		return 3;
	default:
		return 0;
	}
}

int main21(void) {
	char inorder[50] = "1+2-3#"; //"1+((2+3)*4)-5#";//声明中缀(中序)表达式的字符串
	char postorder[50];
	int op = 0;//运算符
	int in_position;//中序表达式的角标
	int po_position;//后续表达式的角标
	int i;
	in_position = 0;
	po_position = 0;
	for (i = 0;i < 50;i++)
		postorder[i] = 0;
	SeqStack sOper;
	init(&sOper);

	while (inorder[in_position]!='#')
	{
		//todo利用栈进行优先级,去括号的过程 
		if (Is_OperatorEx(inorder[in_position])) {
			if (isEmpty(&sOper) || inorder[in_position] == '(') {
				pushStack(&sOper, inorder[in_position]);
			}
			else {
				if (inorder[in_position] == ')') {
					//匹配左括号
					
					while (sOper.stack[sOper.top] != '(') {
						op = popStack(&sOper);
						postorder[po_position++] = op;
					}
					if (sOper.stack[sOper.top] == '(')
					{
						popStack(&sOper);
					}
				}//ctrl+]
				else
				{
					while (PriorityEx(inorder[in_position]) <= PriorityEx(sOper.stack[sOper.top]) && !isEmpty(&sOper)) {
						op = popStack(&sOper);
						postorder[po_position++] = op;
					}
					pushStack(&sOper, inorder[in_position]);
				}
			}

		}
		else {
			postorder[po_position++] = inorder[in_position];
		}

		in_position++;
	}

	while (!isEmpty(&sOper)) {
		op = popStack(&sOper);
		postorder[po_position++] = op;
	}
	for (int i = 0;i < 50;i++) {
		if (postorder[i] == 0)
			break;
		printf("%c", postorder[i]);
	}




	getchar();
	return 0;
}

************************************************************************************************************************************************

顺序存储的二叉树


#include 

//用顺序数组的形式,存储建立一个二叉搜索树
/*
1,我们根据完全二叉树的编号,我们做数组角标1存储输入的第一个元素
2,得到根元素之后,我们开始建立这样的二叉树
	若元素的值大于根节点,则将元素往根节点的右子节点移动,若此节点为空
	那么,存入,否则,直到找到这个空位置
	若元素的值小于根节点,按么则将元素往根节点的左子节点移动,直到找到
	空位存储之
注意,如何寻找节点的左右子节点呢?
用左节点为父节点×2,右子节点为父节点×2+1,
根据这个关系,找到角标,存储之。
*/
void create_btree(int b_tree[], int nodelist[], int len) {
	int i;
	int level;//树的层级,我们用这个level去推进计算将要插入的数字的角标,
	//直白的说,就是确定它的树中位置
	b_tree[1] = nodelist[1];

	for (i = 2;i < len;i++) {
		level = 1;//每一个新元素在二叉搜索树的位置都是通过根节点进行搜索
		while (b_tree[level] != 0) {//0表示当前的子树位置是空的 
			if (nodelist[i] < b_tree[level]) {
				level = level * 2;
			}
			else {
				level = level * 2 + 1;
			}
		}
		//出了循环,说明新元素在二叉搜索树中的位置就定下了
		b_tree[level] = nodelist[i];
	}

}


//若一棵树的左子树所有的值都比根节点的值小,右子树的值都比根大
//这样的二叉树,我们成为二叉查找树(Binary search tree)
int main1(void){
	int b_tree[16] = { 0 };
	int nodelist[16] = { 0, 6,3,8,
						5,2,9,4,7,
						10,0,0,0,
						0,0,0};//根据角标的定义,我们的数组形式不用0开始做编号
	create_btree(b_tree, nodelist, 16);
	for (int i = 1;i < 16;i++) {
		printf("%d,[%d] \n", i, b_tree[i]);
	}
	getchar();
	return 0;
}

二叉链表的存储方式



#include 
#include 
typedef int datatype ;
typedef struct node {
	datatype data;
	struct node *left, *right;	
}BitTree;

//辅助队列Q,这是用来存关系的
BitTree* Q[16];//这是一个指针数组,它将缓存节点的地址,因为这个地址将以
//left域,或者right域进入二叉链表,它本身不维护i,2i,2i+1的关系
//他的关系通过front,rear来维护

//按照直观的建立,我们首先想到的是层次法
//我们根据层次,来逐一将二叉树建立
//输入的数组是按照完全二叉树的编号规则设立,即
//数组角标反映了节点位置关系(存联系)
//逐个扫描数组,直到所有的节点都存取完毕之后,就结束
//约定,0表示数组当前元素为空,最后一个节点标志是-999
BitTree* CreateBinTree(int arr[]) {
	int i = 1;
	//只要我们没有扫描完元素,那么这个二叉链表就没有完成
	//只要扫描,我们就malloc一个节点,然后把这个节点存入left域或者right域
	int front = 1, rear = 0;
	BitTree* root = NULL;
	BitTree* s;//暂存节点
	while (arr[i]!=-999) {
		s = NULL;
		if (arr[i] != 0) {//意味着这个不是空节点,那么我们就要分配空间
			s = (BitTree*)malloc(sizeof(BitTree));
			s->data = arr[i];//存数值
			s->left = NULL;
			s->right = NULL;

		}
		//要让我们新节点入队,进入缓存,等待分配双亲的left域和right域
		Q[++rear] = s;

		if (rear == 1)
		{
			root = s;
		}
		else {
			if (s != NULL && Q[front]) {
				if (rear % 2 == 0) {
					Q[front]->left = s;
				}
				else {
					Q[front]->right = s;
				}
			}
			if (rear % 2 == 1)
				front++;
		}




		i++;
	}
	return root;
}

//树遍历问题:非线性结构的输出问题:前序,中序,后序
void inorder(BitTree *t) {
	if (t) {
		inorder(t->left);
		printf("%d ", t->data);
		inorder(t->right);
	}
}

void preorder(BitTree* t) {
	if (t) {
		//根左右
		printf("%d ", t->data);
		preorder(t->left);
		preorder(t->right);
	}
}

void postorder(BitTree* t) {
	if (t) {
		//左右根
		postorder(t->left);
		postorder(t->right);
		printf("%d ", t->data);
	}
}

int main(void) {

	int arr[17] = {0,6,3,8,
					2,5,7,9,
					0,0,4,0,
					0,0,0,10,
					-999
	};
	BitTree *root = CreateBinTree(arr);
	inorder(root);
	getchar();
	return 0;
}

二叉树非递归遍历的经典解法  BinTreeNonRecvImp



#include 
#include 
typedef int datatype;
typedef struct node {
	datatype data;
	struct node *left, *right;
}BitTree;

//*************************Begin*************
//添加辅助栈的结构:主要是用来保存树的节点
//简言之,这个就是用来存树节点的栈
typedef BitTree BitTreeNode;
typedef int BOOL;
#define TRUE 1
#define FALSE 0


//-——————————修订V0.01——changed by dingst
//BitTreeNode data;那么一定是侵入式的,为什么=》逐个深拷贝每一个分量
//只要是BitTreeNode* Addr;在32位中,这个值永远是4字节
//保存的都是传入的源节点的地址值的一个副本(c/C++语言,都是指拷贝形式)
typedef struct linknode {
	BitTreeNode* _NodeAddrCopy;
	struct linknode* pNext;
}LinkStack;

LinkStack* InitLkStack(void) {
	LinkStack* p = NULL;
	p = (LinkStack*)malloc(sizeof(LinkStack));
	if (p != NULL)
		p->pNext = NULL;
	return p;
}//建立一个带头节点的链表作为链栈

BOOL IsEmptyLkMazeStack(LinkStack* top) {
	return (top->pNext == NULL);
}

LinkStack* PushLkStack(LinkStack* top, BitTreeNode* elem) {
	LinkStack* p = NULL;
	//深拷贝的bug修订,如果传入的是空指针,那么不能访问,修订人丁宋涛
	//elem就是将当前树节点的地址值考一份:比如0x00d500578,我们现在
	//栈就是把这个0x00d500578拷贝一份下来,放到我们_NodeAddrCopy
	//及时栈节点出栈,此时也是我么你的_NodeAddrCopy这个变量被释放
	//与原先的树节点没有关系,不会影响
	//elem是这个函数的形参,它将调用者的实参拷贝一份放入到elem中
	//所以elem就是这个0x00d500578
	//我们用p->_NodeAddrCopy = elem;的操作
	//就把0x00d500578保存下来了,此时他就是原节点地址的一个副本
	if (elem != NULL)
	{
		p = (LinkStack*)malloc(sizeof(LinkStack));
		//todo:我们用新建节点,对传入的二叉树节点做一个深拷贝
		//changed by dingst,我们要做非侵入式的设计
		/*p->data.data = elem->data;
		p->data.left = elem->left;
		p->data.right = elem->right;*/
		p->_NodeAddrCopy = elem;
		p->pNext = top; //p节点尾插入lkStack
		top = p;//top栈顶指针上移
	}



	//我们思考,既然我们允许空指针进入到我们的栈结构,那么我们如何修订
	//我们的出栈?
	//top为空以为着?
	//意味着是否和栈空条件冲突了?
	//如果我们让我们的NULL进入栈空间,那么出栈也会造成空指针
	//这意味着当前的节点不动

	return top;
}

//出栈:pData是传入的一个“临时”空间用来接收出栈的节点信息。
LinkStack* PopLkStack(LinkStack* top, BitTreeNode *pData) {
	LinkStack* p;
	if (top != NULL) {
		//*pData = top->data;
		//----changed by dingst
		/*pData->data = top->data.data;
		pData->left = top->data.left;
		pData->right = top->data.right;*/
		pData = top->_NodeAddrCopy;
		//pData实际上深拷贝了一份节点信息,所以释放栈中节点,不会影响
		//节点数据
		p = top;
		top = p->pNext;
		free(p);
	}
	return top;
}
//*************************End***************



//辅助队列Q,这是用来存关系的
BitTree* Q[16];//这是一个指针数组,它将缓存节点的地址,因为这个地址将以
			   //left域,或者right域进入二叉链表,它本身不维护i,2i,2i+1的关系
			   //他的关系通过front,rear来维护

			   //按照直观的建立,我们首先想到的是层次法
			   //我们根据层次,来逐一将二叉树建立
			   //输入的数组是按照完全二叉树的编号规则设立,即
			   //数组角标反映了节点位置关系(存联系)
			   //逐个扫描数组,直到所有的节点都存取完毕之后,就结束
			   //约定,0表示数组当前元素为空,最后一个节点标志是-999
BitTree* CreateBinTree(int arr[]) {
	int i = 1;
	//只要我们没有扫描完元素,那么这个二叉链表就没有完成
	//只要扫描,我们就malloc一个节点,然后把这个节点存入left域或者right域
	int front = 1, rear = 0;
	BitTree* root = NULL;
	BitTree* s;//暂存节点
	while (arr[i] != -999) {
		s = NULL;
		if (arr[i] != 0) {//意味着这个不是空节点,那么我们就要分配空间
			s = (BitTree*)malloc(sizeof(BitTree));
			s->data = arr[i];//存数值
			s->left = NULL;
			s->right = NULL;

		}
		//要让我们新节点入队,进入缓存,等待分配双亲的left域和right域
		Q[++rear] = s;

		if (rear == 1)
		{
			root = s;
		}
		else {
			if (s != NULL && Q[front]) {
				if (rear % 2 == 0) {
					Q[front]->left = s;
				}
				else {
					Q[front]->right = s;
				}
			}
			if (rear % 2 == 1)
				front++;
		}




		i++;
	}
	return root;
}




//树遍历问题:非线性结构的输出问题:前序,中序,后序
void preorder(BitTree* t) {
	if (t) {
		//根左右
		printf("%d ", t->data);
		preorder(t->left);
		preorder(t->right);
	}
}

void NonRecrvPreOrder(BitTree* root) {
	LinkStack* pTop = NULL;
	//BitTreeNode TempNode;//用来接栈中对应的二叉树节点的
	//-----change by dingst
	BitTreeNode* pNode; //我要让pNode推进树遍历,而且pNode就是
	//真实地址的副本
	pTop = InitLkStack();
	
	//pTop = PushLkStack(pTop, root);
	//---change by dingst
	pNode = root;
	pTop = PushLkStack(pTop, pNode);
	while (!IsEmptyLkMazeStack(pTop)) {
		pNode = pTop->_NodeAddrCopy;//178行的这个赋值操作,就将真实的节点地址赋给了pNode这个函数的局部变量
		pTop = PopLkStack(pTop, pNode);
		if (pNode != NULL) {
			printf("%d ",pNode->data);
			//对右子树压栈
			pTop = PushLkStack(pTop, pNode->right);
			pTop = PushLkStack(pTop, pNode->left);
		}
	}

}


void inorder(BitTree *t) {
	if (t) {
		inorder(t->left);
		printf("%d ", t->data);
		inorder(t->right);
	}
}

/*
中序就是左根右,这就意味着,当我们从根节点出发,应当首先走向左孩子
,而根的左孩子,是根的左子树的根节点,因此
又必须,继续访问其左孩子,直到左孩子为空
当这个节点访问之后,按照相同的规则,访问其右子树
我们借助的栈,就应该按照左根右来压栈
对于任意节点p
1 如果其左孩子不为空,则将p入栈,然后将p的左孩子置为当前的p,然后
再对p进行相同的处理
2 若左孩子为空,则取栈顶元素进行出栈操作,访问当前栈顶节点,然后
将当前的p置为栈顶节点的右孩子
3 直到p为NULL为止
*/
void NonRecvInorder(BitTree *root) {
	LinkStack* pTop = NULL;
	BitTreeNode TempNode;//用来接收栈中的对应的二叉树的节点

	pTop = InitLkStack();
	BitTree *p = root;
	while (p != NULL || !IsEmptyLkMazeStack(pTop)) {
		while (p != NULL) {
			pTop = PushLkStack(pTop, p);
			p = p->left;
		}
		if (!IsEmptyLkMazeStack(pTop)) {
			//因为此时我们在输出的节点信息,就一定要从栈中
			//取出元素
			//这个是取出的元素的_NodeAddrCopy分量就是树中节点的地址
			//因此对这个节点使用->就可以获得真实的数据
			printf("%d ", pTop->_NodeAddrCopy->data);
			p = pTop->_NodeAddrCopy; //因为我们设计的p就是要走完root全部节点,因此
									//p必须获得的是树节点的真实地址
			pTop = PopLkStack(pTop, &TempNode);//当我们把左子树的左叶子节点走完以后,
											   //此时栈顶一定是这个左孩子的根,所以根据左根右,这个根要出栈
			//----change by dingst
											   //p = &TempNode; 因为tmpenode是bitnode的对象,
			//所以tmpenode的地址值可以赋值给p这个指针变量,但是这个tempnode的这个地址值
			//是肯定不等于真实树节点的地址值的
			p = p->right;//走向根的右孩子 
		}
	}
}




void postorder(BitTree* t) {
	if (t) {
		//左右根
		postorder(t->left);
		postorder(t->right);
		printf("%d ", t->data);
	}
}

/*
后序:左右根
根节点只有在左孩子和右孩子都访问以后才能访问
因此,对于任何一个节点,都将其入栈,如果p不存在左孩子和右孩子,直接访问
或者p存在左孩子或者右孩子,但是他的左孩子和右孩子都已经被访问过了
同样的,这个节点也可以直接访问.
如果,上述条件都不满足,就要将这个p的右孩子,左孩子依次入栈
这样就能保证,每次取栈顶元素的时候,左孩子都在右孩子之前被访问,
左右孩子访问之后,根才被访问
*/

void NonRecvPostorder(BitTree* root) {
	BitTree* cur;//当前节点==>cur 保存的就是树节点的地址
	BitTree* pre = NULL;//前一次访问的节点

	LinkStack* pTop = NULL;
	BitTreeNode TempNode;//注意我们就是用tempnode来接里面的内容,他是不能参与地址比较的,他的地址一定和我们栈中的地址不一样
	pTop = InitLkStack();
	pTop = PushLkStack(pTop, root);

	while (!IsEmptyLkMazeStack(pTop)) {
		cur = pTop->_NodeAddrCopy;//拿到栈中深拷贝获得的节点
						  //在访问中,由于我们是深拷贝树的节点,因此,pre和cur的地址不一样,我们只能比较器元素内容
						  //小技巧:利用短路,确保程序的可靠性
		if ((cur->left == NULL && cur->right == NULL) ||
			(pre != NULL && (pre == cur->left|| pre == cur->right))
			) {
			printf("%d ", cur->data);
			pTop = PopLkStack(pTop, &TempNode);
			pre = cur;
		}
		else {
			if (cur->right != NULL)
				pTop = PushLkStack(pTop, cur->right);
			if (cur->left != NULL)
				pTop = PushLkStack(pTop, cur->left);
		}
	}
}

//求叶子节点:树的遍历是一种非线性遍历,在业务编程中,从实现角度上
//用递归遍历三个方法之一,判断节点属性,就可以完成暴力搜索树
int LeafCount = 0;
void LeafCount_DLR(BitTree* t) {
	if (t) {
		if(t->left == NULL && t->right == NULL){
			printf("%d ", t->data);
			LeafCount++;
		}
		LeafCount_DLR(t->left);
		LeafCount_DLR(t->right);
	}
}

//因为我们现在能够不重复,不遗漏获取每一个节点,只有前中后序3种方法,所以,我们首先先写出
//先序递归获取树的深度
int hight = 0;//它可以在多个函数之间共享
//前序是根左右,我们把每一个树从root出发的树,可以看出左右两个子树
//无论我们如何遍历,我们前进到每一个节点都是潜在的根节点。
//我们可以认为每到一个新节点,就是到了一个新树,
//如果说,我们对新节点的发现,我们就认为到了一个新层,那么我们累加
//记录就可以了
//我用level作为当前树的高度
//当我用root,0传入的时候,就意味着已经有第一层的计数起始器
//我们用打擂台的思路,用h比对,就可以完成记录
/*
找一组数中的最大值
for(){
	if(max < a[i]) 
		max =a[i]
	}
*/
int TreeDepth_DLR(BitTree *pNode, int level) {
	if (pNode) {
		level++;
		if (level > hight)
			hight = level;//这一就意味伴随着level的不断深入,我们的hight始终获得的是树中最深的层次
		printf("%d ", pNode->data);
		
		//从327开始到333行,我们认为这个函数的终止条件完成了
		//根据我们前面的观点,递归函数本身就是答案
		//因此hight就应该接递归函数的返回值
		hight = TreeDepth_DLR(pNode->left, level);
		hight = TreeDepth_DLR(pNode->right, level);//因为所有节点的遍历都是跟左右,所以和当前树节点传入的同时,它当前做在的层数我们要传入
	}
	return hight;
}



//根据前序、中序重建二叉树
BitTree* reConstructBinTree(int pre[], int startPre, int endPre,
	int in[], int startIn, int endIn) {
	//如果起始的下标大于结束的下标,那么就退出
	if (startPre > endPre || startIn > endIn) {
		return NULL;
	}

	//前序找到根节点
	BitTree* pNode = (BitTree*)malloc(sizeof(BitTree));
	pNode->left = NULL;
	pNode->right = NULL;
	pNode->data = pre[startPre];//前根当中的首元素一定是当前子树的根节点

	for (int i = startIn;i <= endIn;i++) {
		if (in[i] == pre[startPre]) {
			//i-startIn就是左子树节点的个数
			//i -1就是中序遍历左子树的终点
			
			pNode->left = reConstructBinTree(pre, startPre + 1, startPre + i - startIn, in, startIn, i - 1);
			//前序右子树的遍历起始值:startPre+i-startIn+1
			//中序遍历右子树的起始值:i+1,endIn(i理解成你第几次切刀)
			pNode->right = reConstructBinTree(pre, i - startIn + startPre + 1, endPre, in, i + 1, endIn);
		}
	}
	return pNode;
}


//求根到所有叶子节点的路径
BitTree* Paths[10] = { 0 };


void OutPutPath(){
	int i = 0;
	while (Paths[i] != NULL) {
		printf("%d ", Paths[i]->data);
		i++;
	}
	printf("\n");
}

//求路径的函数
void LeavesPath(BitTree* tree, int level) {
	if (tree == NULL)
		return;
	Paths[level] = tree;//这句话就意味着,只要走到这里,这个子树的根就被记录下来
	//求路径,意味着找到叶子节点,就输出
	if ((tree->left == NULL) && (tree->right == NULL)) {
		//当我确定到已经从树根节点root走到了一个叶子节点,那么它以前所有的根节点都存在paths里面
		Paths[level + 1] = NULL;//通过level传递了路线信息
		OutPutPath();
		return;
	}
	LeavesPath(tree->left, level + 1);
	LeavesPath(tree->right, level + 1);
}


//判断两个二叉树是否相等
//提问如果两个二叉树的前序遍历相等,能否说明,这两个树相等?
//答案是否定的,只有这两个树的 A前序=B前序,A中序=B中序才可以。
BOOL isEqualTree(BitTree* t1, BitTree* t2) {
	if (t1 == NULL && t2 == NULL)
		return TRUE;
	if (!t1 || !t2)//t1,t2一个空一个不空,肯定不相同
		return FALSE;
	if (t1->data == t2->data)
		return isEqualTree(t1->left, t2->left) && isEqualTree(t1->right, t2->right);
	else
		return FALSE;
}

//使用二叉链表建立二叉搜索树:注意,我们目前这个树不支持相同元素
BitTree* insertIntoBinSearchTree(BitTree* root, int node) {
	BitTree* newNode;
	BitTree* currentNode;
	BitTree* parentNode;

	newNode = (BitTree*)malloc(sizeof(BitTree));
	newNode->data = node;
	newNode->left = NULL;
	newNode->right = NULL;

	if (root == NULL)
		return newNode;
	else {
		currentNode = root;//然后我们对当前节点进行定位
		//注意,传入root是根节点,我们要根据根节点,按照左小右大的逻辑找到节点位置
		//首先通过while找到新节点的位置
		while (currentNode != NULL) {
			parentNode = currentNode;
			if (currentNode->data > node) {
				currentNode = currentNode->left;
			}
			else {
				currentNode = currentNode->right;
			}
		}
		//其次,我们将父节点和子节点做连接
		if (parentNode->data > node) {
			parentNode->left = newNode;
		}
		else {
			parentNode->right = newNode;
		}


	}
	return root;
}


BitTree* create_LkBinTree(int data[], int length) {
	BitTree* root=NULL;
	int i;
	for (i = 0;i < length;i++) {
		root = insertIntoBinSearchTree(root, data[i]);
	}
	return root;
}


//二叉树镜像:交换两个子树
void SwapTree(BitTree* tree) {
	BitTree* pTemp;
	pTemp = tree->left;
	tree->left = tree->right;
	tree->right = pTemp;
}

void ExchangeSubTree(BitTree* tree) {
	if (tree == NULL)
		return;
	else {
		SwapTree(tree);
		ExchangeSubTree(tree->left);
		ExchangeSubTree(tree->right);
	}
}

int main(void) {

	int arr[17] = { 0,6,3,8,
		2,5,7,9,
		0,0,4,0,
		0,0,0,10,
		-999
	};
	BitTree *root = CreateBinTree(arr);
	/*printf("递归形式的先跟遍历\n");
	preorder(root);
	printf("\n非递归形式的先跟遍历\n");
	NonRecrvPreOrder(root);*/
	//NonRecvInorder(root);
	/*printf("递归形式的后根遍历\n");
	postorder(root);
	printf("\n非递归形式的后根遍历\n");
	NonRecvPostorder(root);*/

	//******************BEGIN:树的节点与深度****************
	/*LeafCount_DLR(root);
	printf("\n当前书总共有%d个叶子节点", LeafCount);*/
	
	//printf("\n当前树有%d层", TreeDepth_DLR(root, 0));
	给定一个二叉树,我要分别得到它的左子树的深度和右子树的深度
	我们用复用的思想,很容易就得到了内容
	注意,虽然复用的思想是好的,当时要注意全局变量的副作用
	要对全局变量清零
	//hight = 0;
	//printf("\n当前树的左子树有%d层\n", TreeDepth_DLR(root->left, 0));
	//hight = 0;
	//printf("\n当前树的右子树有%d层\n", TreeDepth_DLR(root->right, 0));
	当我们在使用递归遍历中,利用全局变量进行传递的时候
	函数的复用应当注意全局变量的置0
	//******************END 树的节点与深度****************

	//************Beign 二叉树重建
	//前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
	/*int pre[] = { 1,2,4,7,3,5,6,8 };
	int in[] = { 4,7,2,1,5,3,8,6 };
	BitTree* rootNew;
	rootNew = reConstructBinTree(pre, 0, 7, in, 0, 7);
	preorder(rootNew);
	printf("\n");
	inorder(rootNew);*/

	//*************End 二叉树重建

	//***********Begin 获取根节点到叶子节点
	//LeavesPath(root, 0);//0表示层级的传递,只要根据level延伸就能找到每一棵子树的根节点
	//************END获取根节点到叶子节点
	
	//***************Begin  判断两颗二叉树是否一致
	//如果,输入的数字序列前后不一致,那么使用二叉搜索树建立的也不是同一棵树
	/*int arrary[] = { 1,2,3 };
	BitTree* tree1 = create_LkBinTree(arrary, 3);
	int brr[] = { 1,2,3 };
	BitTree* tree2 = create_LkBinTree(brr, 3);

	printf("\n");
	inorder(tree1);
	printf("\n");
	inorder(tree2);
	printf("\n%d", isEqualTree(tree1, tree2));
	*/
	//***************End  判断两颗二叉树是否一致

	//*************Begin 二叉树镜像:交换一个二叉树的左右子树
	int arrary[] = { 5,4,7,2,3,6,8 };
	BitTree* tree1 = create_LkBinTree(arrary, 7);
	printf("\n");
	inorder(tree1);
	ExchangeSubTree(tree1);
	printf("\n");
	inorder(tree1);

	//*************End 二叉树镜像:交换一个二叉树的左右子树
	getchar();
	return 0;
}

二叉树的非递归遍历算法 BitTreeApp



#include 
#include 
typedef int datatype;
typedef struct node {
	datatype data;
	struct node *left, *right;
}BitTree;

//*************************Begin*************
//添加辅助栈的结构:主要是用来保存树的节点
//简言之,这个就是用来存树节点的栈
typedef BitTree BitTreeNode;
typedef int BOOL;
#define TRUE 1
#define FALSE 0


typedef struct linknode {
	BitTreeNode data;
	struct linknode* pNext;
}LinkStack;

LinkStack* InitLkStack(void) {
	LinkStack* p = NULL;
	p = (LinkStack*)malloc(sizeof(LinkStack));
	if (p != NULL)
		p->pNext = NULL;
	return p;
}//建立一个带头节点的链表作为链栈

BOOL IsEmptyLkMazeStack(LinkStack* top) {
	return (top->pNext == NULL);
}

LinkStack* PushLkStack(LinkStack* top, BitTreeNode* elem) {
	LinkStack* p=NULL;
	//深拷贝的bug修订,如果传入的是空指针,那么不能访问,修订人丁宋涛
	if (elem != NULL)
	{
		p = (LinkStack*)malloc(sizeof(LinkStack));
		//todo:我们用新建节点,对传入的二叉树节点做一个深拷贝
		p->data.data = elem->data;
		p->data.left = elem->left;
		p->data.right = elem->right;
		p->pNext = top; //p节点尾插入lkStack
		top = p;//top栈顶指针上移
	}
	
	

	//我们思考,既然我们允许空指针进入到我们的栈结构,那么我们如何修订
	//我们的出栈?
	//top为空以为着?
	//意味着是否和栈空条件冲突了?
	//如果我们让我们的NULL进入栈空间,那么出栈也会造成空指针
	//这意味着当前的节点不动

	return top;
}

//出栈:pData是传入的一个“临时”空间用来接收出栈的节点信息。
LinkStack* PopLkStack(LinkStack* top, BitTreeNode *pData) {
	LinkStack* p;
	if (top != NULL) {
		//*pData = top->data;
		pData->data = top->data.data;
		pData->left = top->data.left;
		pData->right = top->data.right;
		//pData实际上深拷贝了一份节点信息,所以释放栈中节点,不会影响
		//节点数据
		p = top;
		top = p->pNext;
		free(p);
	}
	return top;
}
//*************************End***************



//辅助队列Q,这是用来存关系的
BitTree* Q[16];//这是一个指针数组,它将缓存节点的地址,因为这个地址将以
			   //left域,或者right域进入二叉链表,它本身不维护i,2i,2i+1的关系
			   //他的关系通过front,rear来维护

			   //按照直观的建立,我们首先想到的是层次法
			   //我们根据层次,来逐一将二叉树建立
			   //输入的数组是按照完全二叉树的编号规则设立,即
			   //数组角标反映了节点位置关系(存联系)
			   //逐个扫描数组,直到所有的节点都存取完毕之后,就结束
			   //约定,0表示数组当前元素为空,最后一个节点标志是-999
BitTree* CreateBinTree(int arr[]) {
	int i = 1;
	//只要我们没有扫描完元素,那么这个二叉链表就没有完成
	//只要扫描,我们就malloc一个节点,然后把这个节点存入left域或者right域
	int front = 1, rear = 0;
	BitTree* root = NULL;
	BitTree* s;//暂存节点
	while (arr[i] != -999) {
		s = NULL;
		if (arr[i] != 0) {//意味着这个不是空节点,那么我们就要分配空间
			s = (BitTree*)malloc(sizeof(BitTree));
			s->data = arr[i];//存数值
			s->left = NULL;
			s->right = NULL;

		}
		//要让我们新节点入队,进入缓存,等待分配双亲的left域和right域
		Q[++rear] = s;

		if (rear == 1)
		{
			root = s;
		}
		else {
			if (s != NULL && Q[front]) {
				if (rear % 2 == 0) {
					Q[front]->left = s;
				}
				else {
					Q[front]->right = s;
				}
			}
			if (rear % 2 == 1)
				front++;
		}




		i++;
	}
	return root;
}




//树遍历问题:非线性结构的输出问题:前序,中序,后序
void preorder(BitTree* t) {
	if (t) {
		//根左右
		printf("%d ", t->data);
		preorder(t->left);
		preorder(t->right);
	}
}

void NonRecrvPreOrder(BitTree* root) {
	LinkStack* pTop = NULL;
	BitTreeNode TempNode;//用来接栈中对应的二叉树节点的
	pTop = InitLkStack();
	pTop = PushLkStack(pTop, root);

	while (!IsEmptyLkMazeStack(pTop)) {
		pTop = PopLkStack(pTop, &TempNode);
		if (&TempNode != NULL) {
			printf("%d ", TempNode.data);
			//对右子树压栈
			pTop = PushLkStack(pTop, TempNode.right);
			pTop = PushLkStack(pTop, TempNode.left);
		}
	}

}


void inorder(BitTree *t) {
	if (t) {
		inorder(t->left);
		printf("%d ", t->data);
		inorder(t->right);
	}
}

/*
中序就是左根右,这就意味着,当我们从根节点出发,应当首先走向左孩子
,而根的左孩子,是根的左子树的根节点,因此
又必须,继续访问其左孩子,直到左孩子为空
当这个节点访问之后,按照相同的规则,访问其右子树
我们借助的栈,就应该按照左根右来压栈
对于任意节点p
1 如果其左孩子不为空,则将p入栈,然后将p的左孩子置为当前的p,然后
再对p进行相同的处理
2 若左孩子为空,则取栈顶元素进行出栈操作,访问当前栈顶节点,然后
将当前的p置为栈顶节点的右孩子
3 直到p为NULL为止
*/
void NonRecvInorder(BitTree *root) {
	LinkStack* pTop = NULL;
	BitTreeNode TempNode;//用来接收栈中的对应的二叉树的节点

	pTop = InitLkStack();
	BitTree *p = root;
	while (p != NULL || !IsEmptyLkMazeStack(pTop)) {
		while (p != NULL) {
			pTop = PushLkStack(pTop, p);
			p = p->left;
		}
		if (!IsEmptyLkMazeStack(pTop)) {
			printf("%d ", pTop->data.data);
			pTop = PopLkStack(pTop, &TempNode);//当我们把左子树的左叶子节点走完以后,
			//此时栈顶一定是这个左孩子的根,所以根据左根右,这个根要出栈
			p = &TempNode;
			p = p->right;//走向根的右孩子 
		}
	}
}




void postorder(BitTree* t) {
	if (t) {
		//左右根
		postorder(t->left);
		postorder(t->right);
		printf("%d ", t->data);
	}
}

/*
后序:左右根
根节点只有在左孩子和右孩子都访问以后才能访问
因此,对于任何一个节点,都将其入栈,如果p不存在左孩子和右孩子,直接访问
或者p存在左孩子或者右孩子,但是他的左孩子和右孩子都已经被访问过了
同样的,这个节点也可以直接访问.
如果,上述条件都不满足,就要将这个p的右孩子,左孩子依次入栈
这样就能保证,每次取栈顶元素的时候,左孩子都在右孩子之前被访问,
左右孩子访问之后,根才被访问
*/

void NonRecvPostorder(BitTree* root) {
	BitTree* cur;//当前节点
	BitTree* pre = NULL;//前一次访问的节点

	LinkStack* pTop = NULL;
	BitTreeNode TempNode;
	pTop = InitLkStack();
	pTop = PushLkStack(pTop, root);

	while (!IsEmptyLkMazeStack(pTop)) {
		cur = &pTop->data;//拿到栈中深拷贝获得的节点
		//在访问中,由于我们是深拷贝树的节点,因此,pre和cur的地址不一样,我们只能比较器元素内容
		//小技巧:利用短路,确保程序的可靠性
		if ((cur->left == NULL && cur->right == NULL) ||
			(pre != NULL && ((cur->left  && pre->data == cur->left->data) || (cur->right && pre->data == cur->right->data)))
			) {
			printf("%d ", cur->data);
			pTop = PopLkStack(pTop, &TempNode);
			pre = &TempNode;
		}
		else {
			if (cur->right != NULL)
				pTop = PushLkStack(pTop, cur->right);
			if (cur->left != NULL)
				pTop = PushLkStack(pTop, cur->left);
		}
	}
}



int main(void) {

	int arr[17] = { 0,6,3,8,
		2,5,7,9,
		0,0,4,0,
		0,0,0,10,
		-999
	};
	BitTree *root = CreateBinTree(arr);
	/*printf("递归形式的先跟遍历\n");
	preorder(root);
	printf("\n非递归形式的先跟遍历\n");
	NonRecrvPreOrder(root);*/
	//NonRecvInorder(root);
	printf("递归形式的后根遍历\n");
	postorder(root);
	printf("\n非递归形式的后根遍历\n");
	NonRecvPostorder(root);
	getchar();
	return 0;
}

 

HuffmanTree 



#include 
#include 
typedef int ElemType;
typedef struct BTreeNode {
	ElemType data;
	struct  BTreeNode* left, *right;
	
}BTreeNode;

//根据传入的数组a,其中n个元素作为其权值,构建一颗哈夫曼树,返回根节点
BTreeNode* CreateHuffman(ElemType a[], int n) {
	int i, j;
	BTreeNode ** b, *q;
	b = (BTreeNode **)malloc(n * sizeof(BTreeNode));
	for (i = 0;i < n;i++) {
		b[i] = (BTreeNode*)malloc(sizeof(BTreeNode));
		b[i]->data = a[i];
		b[i]->left = b[i]->right = NULL;
	}
	//以上通过构建连续存储空间,拿到了需要存储的n个huffman节点,然后根据
	//逐个将节点加入的过程,完成哈夫曼树

	for (i = 1;i < n;i++) {
		//k1:森林中具有最小权值的树根节点的下标,k2为次小下标
		int k1 = -1, k2;
		//让k1指向第一棵树,k2指向第二棵
		for (j = 0;j < n;j++) {
			if (b[j] != NULL && k1 == -1) {
				k1 = j;
				continue;
			}
			if (b[j] != NULL) {
				k2 = j;
				break;
			}
		}
		//从当前森林中求出最小权值树、次小的
		for (j = k2;j < n;j++) {
			if (b[j] != NULL) {
				if (b[j]->data < b[k1]->data) {
					k2 = k1;
					k1 = j;
				}
				else {
					if (b[j]->data < b[k2]->data)
						k2 = j;
				}
			}
		}

		//由k1,k2拼树,q指向树根节点
		q =(BTreeNode*) malloc(sizeof(BTreeNode));
		q->data = b[k1]->data + b[k2]->data;
		q->left = b[k1];
		q->right = b[k2];
		
		b[k1] = q;//将新树节点放回原来b申请的数组k1的位置 
		b[k2] = NULL;
	}
	free(b);
	return q;//返回整个Huffman树的根节点
}

//求Huffman 树的路径

ElemType WeightPathLength(BTreeNode* FBT,int len){
	if (FBT == NULL)
		return 0;
	else
	{
		if (FBT->left == NULL && FBT->right == NULL) {
			//到叶子节点
			return FBT->data * len;
		}
		else {
			//非叶子节点,递归,返回左右子树带权路径长度的和,len要递增
			return WeightPathLength(FBT->left, len + 1) + WeightPathLength(FBT->right, len + 1);
		}
	}
}

//求Huffman编码
void HuffmanCoding(BTreeNode* FBT, int len) {
	static int a[10];//定义一个静态数组,保存每一个叶子节点的编码
	if (FBT != NULL) {
		if (FBT->left == NULL && FBT->right == NULL) {
			//进入到了叶子节点
			int i;
			printf("节点权值为%d的编码是:", FBT->data);
			for (i = 0;i < len;i++) {
				printf("%d", a[i]);
			}
			printf("\n");
		}
		else {
			//访问到的是非叶子节点,那么就要按照分支中,左0,右1的方式递归调用,存入a数组中
			a[len] = 0;
			HuffmanCoding(FBT->left, len + 1);
			a[len] = 1;
			HuffmanCoding(FBT->right, len + 1);
		}
	}
}
int main() {

	int n = 6, i;
	BTreeNode* fbt;
	int arr[] = { 3,9,5,12,6,15 };
	fbt = CreateHuffman(arr, n);
	printf("带权路径长度%d\n", WeightPathLength(fbt, 0));
	HuffmanCoding(fbt, 0);
	getchar();
	return 0;
}

 

你可能感兴趣的:(趣学算法)