期末、考研数据结构复习重点代码——超详细,代码可以运行

前言

  • 本文中使用数据类型时,若无特殊需求,节点数据默认都采用int类型。

线性表

顺序表

定义

//静态定义
#define MaxSize 50
typedef struct{
	int data[MaxSize];//采用数组的形式定义
	int length;	//表示顺序表的长度
}seqList;

//动态定义
#define InitSize 50
typedef struct{
	int * data;	//采用指针的形式定义
	int MaxSize,length;	//顺序表的最大长度以及当前顺序表的长度
}seqList;

插入

/*数组定义的形式*/
/**
* 按照指定位置插入元素
* &L 顺序表
* i 插入的位置(合法的位置为 1 <= i <= length+1)
* e 待插入的元素
*/
bool insertList(seqList & L, int i, int e) {
    if (i < 1 || i > L.length + 1)
        return false; //插入位置不合法
    if (L.length >= MaxSize) //顺序表当前长度等于最大长度
        return false; //顺序表空间已满
    for (int j = L.length; j >= i; j--) { //将第i个元素及以后的元素依次向后移动
        L.data[j] = L.data[j - 1];
    }
    L.data[i - 1] = e;//插入元素
    L.length++;//顺序表长度加一
    return true;
}

删除

/*数组定义的形式*/
/**
* 按照指定位置删除元素
* &L 顺序表
* i 删除的位置(合法的位置为 1 <= i <= length)
* e 删除的元素
*/
bool deleteList(seqList & L, int i, int & e) {
    if (i < 1 || i > L.length) //位置不合法
        return false;
    e = L.data[i - 1]; //将被删除的元素赋值给e,以便删除结束后知道删除的元素
    for (int j = i; j < L.length; j++) { //开始删除
        L.data[j - 1] = L.data[j]; //待删除元素的后一个元素依次覆盖前一个元素
    }
    L.length--;
    return true;
}

查找元素

/*数组定义的形式*/
/**
* 采用遍历的方式查找
* &L 顺序表
* e 查找的元素
*/
int findList(seqList & L, int e) {
    for (int i = 0; i < L.length; i++) {
        if (L.data[i] == e)
            return i + 1; //返回的是元素位置
    }
    return 0; //未找到元素,返回0
}

链表

定义

//单链表定义
typedef struct LNode {
	int data;//节点数据
	struct LNode *next;//下一个节点指针
} LNode, *LinkList;

创建单链表

带头结点的方式

采用带头结点的方式创建链表(头插法)

//采用带头结点的方式创建链表(头插法)
void createListForHead(LinkList &L) {
	LNode *s;//待插入的节点
	int e;//待插入的元素
	L = (LinkList)malloc(sizeof(LNode));//为链表分配空间
	L->next = NULL;//初始化为空
	while (true) {
		printf("请输入当前要插入的元素数据:");
		scanf("%d", &e);//输入待插入的元素
		if (e == -1)//循环退出条件
			break;
		s = (LNode * )malloc(sizeof(LNode));//为待插入节点分配空间
		s->data = e;//将输入的元素赋值到节点的数据域
		s->next = L->next;//修改指针指向(新节点的下一个节点指针指向头结点的下一个节点指针)
		L->next = s;//修改指针指向(头结点的下一个节点指针指向新的节点)
	}
}

采用带头结点的方式创建链表(尾插法)

//采用带头结点的方式创建链表(尾插法)
void createListForRear(LinkList &L) {
	LNode *s, *r; //待插入的节点和尾指针
	int e;//待插入的元素
	L = (LinkList)malloc(sizeof(LNode));//为链表分配空间
	r = L;//初始时,尾指针指向头结点
	while (true) {
		printf("请输入当前要插入的元素数据:");
		scanf("%d", &e);//输入待插入的元素
		if (e == -1)//循环退出条件
			break;
		s = (LNode *)malloc(sizeof(LNode));//为待插入节点分配空间
		s->data = e;//将输入的元素赋值到节点的数据域
		r->next = s;//尾指针的下一个节点指向待插入元素 
		r = s;//修改尾指针指向(指向新节点) 
	}
	r->next = NULL;//将尾指针的下一个节点置空,表示当前即为最后一个元素 
}

查找节点

按位置查找节点

//按位置查找节点
LNode * getNodeByLocate(LinkList &L, int i) {
	if (i == 0)
		return L;//返回头结点
	if (i < 1)
		return NULL;//位置非法,返回空
	LNode *p = L->next;//p指向第一个节点
	int j = 1;//设置计数从1开始
	while (p && j < i) {//从1开始找,p不为NULL时说明有元素,j<i说明还未到要找到的元素位置
		p = p->next;//指向下一个元素
		j++;//计数加一
	}
	return p;//若找到,返回的就是该节点;若未找到就会返回NULL
}

按值查找节点

//按值查找节点
LNode * getNodeByElem(LinkList &L, int e) {
	LNode *p = L->next;//p指向第一个节点
	while (p && p->data != e) {//从第一个节点开始找。判断当前节点的数据是否与待查找数据一致 
		p = p->next;//不一致时,指针下移。指针移到最后时,p就是NULL 
	}
	return p;//返回找到的节点。如果未找到,p指向的就是NULL 
}

插入节点

给定位置插入节点

//插入节点(给定位置插入节点)
/*
* 注意:以下代码为核心部分,未进行插入位置合法性校验
* &L 插入的链表地址
* i 插入的位置
* *s 插入的节点
*/
void insertLNodeByLocate(LinkList &L, int i, LNode *s) {
	LNode *p = getNodeByLocate(L, i - 1); //查找待插入位置的前一个节点
	s->next  = p->next;
	p->next = s;
}

给定节点p之前插入一个节点s

//插入节点(给定节点p之前插入一个节点s)
/*
* 注意:以下代码为核心部分,未进行插入位置合法性校验
* *p 给定节点
* *s 插入的节点
*/
void insertLNodeByNode(LNode *p, LNode *s) {
	//交换数据
	int temp = p->data;
	p->data = s->data;
	s->data = temp;
	//修改指针
	s->next = p->next;
	p->next = s;
}

删除

删除给定位置的节点

//删除给定位置的节点
bool removeLNodeByLocate(LinkList &L, int i) {
	LNode *p = getNodeByLocate(L, i - 1); //查找待删除节点的前一个结点
	LNode *q = p->next;//指向待删除节点
	p->next = q->next;//修改指针
	free(q);//释放指针
	return true;
}

删除指定节点

//删除指定节点
/*
* 示意过程
* 1->2->(3)->4->5->NULL(原始链表)删除3号节点
* 1->2->(4)->4->5->NULL(复制3号节点下一个节点的数据到三号节点)
* 1->2->(4)->5->NULL(删除3号节点的下一个结点,就相当于形式上删除了3号节点)
*/
bool removeLNodeByNode(LNode *p) {
	LNode *q = p->next;//指定节点的下一个结点
	p->data = q->data;//将下一个节点的数据复制到待删除节点,然后将下一个结点删除即可
	p->next = q->next;//修改节点指向
	free(q);
	return true;
}

双链表

定义

//双链表定义
typedef struct DNode{
	int data;//节点数据
	struct DNode *prior,*next;//指向前一个节点的指针和指向后一个结点的指针
}DNode,*DLinkList;

插入

将节点s插入到结点p之后

/**
 * @brief 双链表插入数据(将节点s插入到结点p之后)
 *
 * @param p 给定节点
 * @param s 待插入节点
 *
 * @return 插入是否成功
 **/
bool insertDNode(DNode *p, DNode *s) {
	if (p->next == NULL) { //插入节点为最后一个结点
		p->next = s;
		s->prior = p;
		s->next = NULL;
	} else {
		s->next = p->next;
		p->next->prior = s;
		s->prior = p;
		p->next = s;
	}
	return true;
}

删除

删除所给节点的后一个节点

/**
 * @brief 删除所给节点的后一个节点
 *
 * @param p 所给节点
 *
 * @return 删除结果
 **/
bool removeDNode(DNode *p) {
	DNode *q = p->next;
	if (q != NULL) {
		free(q);
		q->next->prior = p;
		p->next = q->next;
		free(q);
	}
	return true;
}

链表代码汇总

#include <stdio.h>
#include <stdlib.h>
//单链表定义
typedef struct LNode {
	int data;//节点数据
	struct LNode *next;//下一个节点指针
} LNode, *LinkList;

//双链表定义
typedef struct DNode {
	int data;//节点数据
	struct DNode *prior, *next; //指向前一个节点的指针和指向后一个结点的指针
} DNode, *DLinkList;

//采用带头结点的方式创建链表(头插法)
void createListForHead(LinkList &L) {
	LNode *s;//待插入的节点
	int e;//待插入的元素
	L = (LinkList)malloc(sizeof(LNode));//为链表分配空间
	L->next = NULL;//初始化为空
	while (true) {
		printf("请输入当前要插入的元素数据:");
		scanf("%d", &e);//输入待插入的元素
		if (e == -1)//循环退出条件
			break;
		s = (LNode * )malloc(sizeof(LNode));//为待插入节点分配空间
		s->data = e;//将输入的元素赋值到节点的数据域
		s->next = L->next;//修改指针指向(新节点的下一个节点指针指向头结点的下一个节点指针)
		L->next = s;//修改指针指向(头结点的下一个节点指针指向新的节点)
	}
}


//采用带头结点的方式创建链表(尾插法)
void createListForRear(LinkList &L) {
	LNode *s, *r; //待插入的节点和尾指针
	int e;//待插入的元素
	L = (LinkList)malloc(sizeof(LNode));//为链表分配空间
	r = L;//初始时,尾指针指向头结点
	while (true) {
		printf("请输入当前要插入的元素数据:");
		scanf("%d", &e);//输入待插入的元素
		if (e == -1)//循环退出条件
			break;
		s = (LNode *)malloc(sizeof(LNode));//为待插入节点分配空间
		s->data = e;//将输入的元素赋值到节点的数据域
		r->next = s;//尾指针的下一个节点指向待插入元素
		r = s;//修改尾指针指向(指向新节点)
	}
	r->next = NULL;//将尾指针的下一个节点置空,表示当前即为最后一个元素
}

//按位置查找节点
LNode * getNodeByLocate(LinkList &L, int i) {
	if (i == 0)
		return L;//返回头结点
	if (i < 1)
		return NULL;//位置非法,返回空
	LNode *p = L->next;//p指向第一个节点
	int j = 1;//设置计数从1开始
	while (p && j < i) {//从1开始找,p不为NULL时说明有元素,j<i说明还未到要找到的元素位置
		p = p->next;//指向下一个元素
		j++;//计数加一
	}
	return p;//若找到,返回的就是该节点;若未找到就会返回NULL

}

//按值查找节点
LNode * getNodeByElem(LinkList &L, int e) {
	LNode *p = L->next;//p指向第一个节点
	while (p && p->data != e) {//从第一个节点开始找。判断当前节点的数据是否与待查找数据一致
		p = p->next;//不一致时,指针下移。指针移到最后时,p就是NULL
	}
	return p;//返回找到的节点。如果未找到,p指向的就是NULL
}

//插入节点(给定位置插入节点)
/*
* &L 插入的链表地址
* i 插入的位置
* *s 插入的节点
*/
bool insertLNodeByLocate(LinkList &L, int i, LNode *s) {
	LNode *p = getNodeByLocate(L, i - 1); //查找待插入位置的前一个节点
	s->next  = p->next;
	p->next = s;
	return true;
}

//插入节点(给定节点p之前插入一个节点s)
/*
* *p 给定节点
* *s 插入的节点
*/
bool insertLNodeByNode(LNode *p, LNode *s) {
	//交换数据
	int temp = p->data;
	p->data = s->data;
	s->data = temp;
	//修改指针
	s->next = p->next;
	p->next = s;
	return true;
}

//删除给定位置的节点
bool removeLNodeByLocate(LinkList &L, int i) {
	LNode *p = getNodeByLocate(L, i - 1); //查找待删除节点的前一个结点
	LNode *q = p->next;//指向待删除节点
	p->next = q->next;//修改指针
	free(q);//释放指针
	return true;
}

//删除指定节点
/*
* 示意过程
* 1->2->(3)->4->5->NULL(原始链表)删除3号节点
* 1->2->(4)->4->5->NULL(复制3号节点下一个节点的数据到三号节点)
* 1->2->(4)->5->NULL(删除3号节点的下一个结点,就相当于形式上删除了3号节点)
*/
bool removeLNodeByNode(LNode *p) {
	LNode *q = p->next;//指定节点的下一个结点
	p->data = q->data;//将下一个节点的数据复制到待删除节点,然后将下一个结点删除即可
	p->next = q->next;//修改节点指向
	free(q);
	return true;
}

//获取单链表长度
int getListLength(LinkList &L) {
	LNode *p = L->next;
	int count = 0;
	while (p) {
		count++;
		p = p->next;
	}
	return count;
}

//输出单链表元素
void showList(LinkList L) {
	LNode *p = L->next;
	while (p) {
		if (p->next) {//有下一个节点时,就输出箭头
			printf("%d -> ", p->data);
		} else {
			printf("%d\n", p->data);
		}
		p = p->next;
	}
}

//创建带测试数据的单链表(尾插法)
void  createListForRearWithTestData(LinkList &L) {
	LNode *s, *r; //待插入的节点和尾指针
//	int data[] = {5, 3, 1, 2, 4, 6, 9, 7, 8, 10}; //待插入的元素(无序)
	int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; //待插入的元素(有序)
	L = (LinkList)malloc(sizeof(LNode));//为链表分配空间
	r = L;//初始时,尾指针指向头结点
	int len = sizeof(data) / sizeof(int);//数组元素长度
	for (int i = 0; i < len; i++) {
		s = (LNode *)malloc(sizeof(LNode));//为待插入节点分配空间
		s->data = data[i];//将输入的元素赋值到节点的数据域
		r->next = s;//尾指针的下一个节点指向待插入元素
		r = s;//修改尾指针指向(指向新节点)
	}
	r->next = NULL;//将尾指针的下一个节点置空,表示当前即为最后一个元素
}

//双链表创建并初始数据
void createDLinkList(DLinkList &DL) {
	//	int data[] = {5, 3, 1, 2, 4, 6, 9, 7, 8, 10}; //待插入的元素(无序)
	int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; //待插入的元素(有序)
	DL = (DNode *)malloc(sizeof(DNode));
	DL->prior = NULL;
	DL->next = NULL;
	int len = sizeof(data) / sizeof(int);//数组元素长度
//	DNode *p = s->next;
	DNode *s;
	for (int i = 0; i < len; i++) {
		s = (DNode *)malloc(sizeof(DNode));
		s->data = data[i];
		if (DL->next == NULL) {
			DL->next = s;
			s->prior = DL;
			s->next = NULL;
		} else {
			s->next = DL->next;
			DL->next->prior = s;
			s->prior = DL;
			DL->next = s;
		}
	}
}

//显示双链表中的数据
void showDLinkList(DLinkList &DL) {
	DNode *p = DL->next;
	while (p) {
		if (p->next) {
			printf("%d <=> ", p->data);
		} else {
			printf("%d\n", p->data);
		}
		p = p->next;
	}
}


/**
 * @brief 双链表插入数据(将节点s插入到结点p之后)
 *
 * @param p 给定节点
 * @param s 待插入节点
 *
 * @return 插入是否成功
 **/
bool insertDNode(DNode *p, DNode *s) {
	if (p->next == NULL) { //插入节点为最后一个结点
		p->next = s;
		s->prior = p;
		s->next = NULL;
	} else {
		s->next = p->next;
		p->next->prior = s;
		s->prior = p;
		p->next = s;
	}
	return true;
}

/**
 * @brief 删除所给节点的后一个节点
 *
 * @param p 所给节点
 *
 * @return 删除结果
 **/
bool removeDNode(DNode *p) {
	DNode *q = p->next;
	if (q != NULL) {
		free(q);
		q->next->prior = p;
		p->next = q->next;
		free(q);
	}
	return true;
}

int main() {
	/*
	LinkList L ;
	//	createListForHead(L);
	//	createListForRear(L);
	createListForRearWithTestData(L);
	printf("当前链表中节点信息为:\n");
	showList(L);
	printf("链表长度为:%d\n", getListLength(L));
	//	LNode * node =  getNodeByLocate(L, 10);
	//	printf("找到的元素为:节点地址:%p,节点元素:%d\n", node, node->data);
	//	printf("*****************华丽的分割线*****************\n");
	//	LNode * node2 =  getNodeByElem(L, 50);
	//	printf("找到的元素为:节点地址:%p,节点元素:%d\n", node2, node2->data);
	//	printf("*****************华丽的分割线*****************\n");
	//	LNode *s = (LNode *)malloc(sizeof(LNode));
	//	s->data = 666;
	//	insertLNodeByLocate(L, 3, s);
	//	printf("在3号位置插入节点后,链表中节点信息为:\n");
	//	showList(L);
	//	printf("链表长度为:%d\n", getListLength(L));
	printf("*****************华丽的分割线*****************\n");
	//	LNode *p = getNodeByLocate(L, 3); //查找一个节点
	//	LNode *s = (LNode *)malloc(sizeof(LNode));
	//	s->data = 666;
	//	insertLNodeByNode(p, s);
	//	printf("在3号节点前插入节点后,链表中节点信息为:\n");
	//	removeLNodeByLocate(L, 3);
	LNode *p = getNodeByLocate(L, 3); //查找一个节点
	removeLNodeByNode(p);
	printf("删除3号节点后链表信息为:\n");
	showList(L);
	printf("链表长度为:%d\n", getListLength(L));
	*/
	DLinkList DL;
	createDLinkList(DL);
	showDLinkList(DL);
	DNode *p = DL->next->next->next;
//	printf("%d节点前一个结点为:%d", p->data, p->prior->data);
//	DNode *s = (DNode *) malloc(sizeof(DNode));
//	s->data = 666;
//	insertDNode(p, s);
//	printf("插入节点后,双链表信息为:\n");
//	showDLinkList(DL);
//	printf("新插入的节点前一个结点数据为:%d", s->prior->data);
	removeDNode(p);
	showDLinkList(DL);
	return 0;
}

栈和队列

顺序栈

定义

//顺序栈的最大容量
#define MaxSize 50

//顺序栈定义
typedef struct {
	int data[MaxSize];//存放栈中的元素
	int top;//栈顶指针
} SeqStack;

初始化栈

//初始化栈
void InitSeqStack(SeqStack &S) {
	S.top = 0;//栈顶指针指向0。若栈顶指针指向-1,后续操作需要做出相应的调整
}

栈判空

//判断栈是否为空
bool emptySeqStack(SeqStack &S) {
	return S.top == 0;//等于0即为空
}

进栈

/**
 * @brief 进栈
 *
 * @param S 顺序栈
 * @param e 待进栈元素
 *
 * @return 操作结果
 **/
bool pushSeqStack(SeqStack &S, int e) {
	if (S.top == MaxSize)//栈满
		return false;
	S.data[S.top++] = e;//将待进栈的元素添加到栈中就,并修改栈顶指针
	return true;
}

出栈

/**
 * @brief 出栈
 *
 * @param S 顺序栈
 *
 * @return 出栈元素
 **/
int popSeqStack(SeqStack &S) {
	if (emptySeqStack(S))
		return -1;
	return S.data[--S.top];//取出栈顶元素,并修改栈顶指针
}

获取栈顶元素

//获取栈顶元素
int getTop(SeqStack &S) {
	int _top = S.top;//记录当前栈顶指针,避免在后面操作修改到栈顶指针
	return S.data[_top - 1];
}

顺序栈代码汇总

#include <stdio.h>
#include <stdlib.h>

//顺序栈的最大容量
#define MaxSize 50

//顺序栈定义
typedef struct {
	int data[MaxSize];//存放栈中的元素
	int top;//栈顶指针
} SeqStack;

//初始化栈
void InitSeqStack(SeqStack &S) {
	S.top = 0;//栈顶指针指向0。若栈顶指针指向-1,后续操作需要做出相应的调整
}

//判断栈是否为空
bool emptySeqStack(SeqStack &S) {
	return S.top == 0;//等于0即为空
}

/**
 * @brief 进栈
 *
 * @param S 顺序栈
 * @param e 待进栈元素
 *
 * @return 操作结果
 **/
bool pushSeqStack(SeqStack &S, int e) {
	if (S.top == MaxSize)//栈满
		return false;
	S.data[S.top++] = e;//将待进栈的元素添加到栈中就,并修改栈顶指针
	return true;
}

/**
 * @brief 出栈
 *
 * @param S 顺序栈
 *
 * @return 出栈元素
 **/
int popSeqStack(SeqStack &S) {
	if (emptySeqStack(S))
		return -1;
	return S.data[--S.top];//取出栈顶元素,并修改栈顶指针
}

//获取栈顶元素
int getTop(SeqStack &S) {
	int _top = S.top;//记录当前栈顶指针,避免在后面操作修改到栈顶指针
	return S.data[_top - 1];
}

//显示栈中元素
void showSeqStack(SeqStack &S) {
	for (int i = S.top - 1; i >= 0; i--) {
		if (i == S.top - 1) {
			printf("    |----------|\n");
		}
		printf("    |   %3d    |\n", S.data[i]);
		printf("    |----------|\n");
	}
}

int main() {
	SeqStack S;
	InitSeqStack(S);
	for (int i = 1; i <= 10; i++) {
		pushSeqStack(S, i);
	}
	showSeqStack(S);
	for (int i = 0; i < 5; i++) {
		printf("当前栈顶元素为:%d\n", popSeqStack(S));
	}
	printf("<=====出栈后,栈中信息=====>\n");
	showSeqStack(S);
	printf("当前栈顶元素为:%d\n", getTop(S));
	showSeqStack(S);
	return 0;
}

链栈

定义

//链栈节点定义
typedef struct SNode {
	int data;//数据元素
	struct SNode *next;//下一个节点指针
} SNode;

//链栈定义
typedef struct {
	SNode *top;//栈顶指针
}*LinkStack;

初始化

//初始化栈
void initStack(LinkStack &LS) {
	LS = (LinkStack)malloc(sizeof(LinkStack));//分配空间
	LS->top = NULL;//初始化时,栈顶指针指向空
}

进栈

/**
 * @brief 进栈(类似于单链表的头插法)
 *
 * @param LS 链栈
 * @param e 待进栈元素
 **/
void pushStack(LinkStack &LS, int e) {
	SNode *s = (SNode *)malloc(sizeof(SNode));//为节点分配一个空间
	s->data = e;//将数据赋值到节点里
	s->next = LS->top;//新节点的下一个指向为栈顶指针的下一个指向
	LS->top = s;//将栈顶指针指向新的节点
}

出栈

/**
 * @brief 出栈
 *
 * @param LS 链栈
 *
 * @return 栈顶元素
 **/
int popStack(LinkStack &LS) {
	SNode *p = LS->top;//记录栈顶指针
	if (p == NULL) {//空栈时返回-1
		return -1;
	}
	int e =  p->data;//记录栈顶元素
	SNode *q = p->next;//记录栈顶指针的下一个节点
	LS->top = q;//修改栈顶指针
	free(p);//释放空间
	return e;//返回栈顶元素
}

链栈代码汇总

#include <stdio.h>
#include <stdlib.h>

//链栈节点定义
typedef struct SNode {
	int data;//数据元素
	struct SNode *next;//下一个节点指针
} SNode;

//链栈定义
typedef struct {
	SNode *top;//栈顶指针
}*LinkStack;

//初始化栈
void initStack(LinkStack &LS) {
	LS = (LinkStack)malloc(sizeof(LinkStack));//分配空间
	LS->top = NULL;//初始化时,栈顶指针指向空
}

/**
 * @brief 进栈(类似于单链表的头插法)
 *
 * @param LS 链栈
 * @param e 待进栈元素
 **/
void pushStack(LinkStack &LS, int e) {
	SNode *s = (SNode *)malloc(sizeof(SNode));//为节点分配一个空间
	s->data = e;//将数据赋值到节点里
	s->next = LS->top;//新节点的下一个指向为栈顶指针的下一个指向
	LS->top = s;//将栈顶指针指向新的节点
}

/**
 * @brief 出栈
 *
 * @param LS 链栈
 *
 * @return 栈顶元素
 **/
int popStack(LinkStack &LS) {
	SNode *p = LS->top;//记录栈顶指针
	if (p == NULL) {//空栈时返回-1
		return -1;
	}
	int e =  p->data;//记录栈顶元素
	SNode *q = p->next;//记录栈顶指针的下一个节点
	LS->top = q;//修改栈顶指针
	free(p);//释放空间
	return e;//返回栈顶元素
}

void showStack(LinkStack &LS) {
	SNode *p = LS->top;
	while (p != NULL) {
		if (p->next) {
			printf("%d -> ", p->data);
		} else {
			printf("%d\n", p->data);
		}
		p = p->next;
	}
}

int main() {
	LinkStack LS;
	initStack(LS);
	for (int i = 1; i <= 10; i++) {
		pushStack(LS, i);
	}
	showStack(LS);
	printf("出栈元素为:%d\n", popStack(LS));
	showStack(LS);
	return 0;
}

循环队列

定义

//定义队列最大个数(采用循环队列时,需要牺牲一个空间来方便处理队满的情况)
#define MaxSize 11

//定义队列
typedef struct {
	int data[MaxSize];//存放队列数据
	int front, rear; //队头、队尾指针
} SeqQueue;

初始化

//初始化
void initQueue(SeqQueue &Q) {
	Q.front = 0;
	Q.rear = 0;
}

判空

//队空判断
bool emptyQueue(SeqQueue &Q) {
	return Q.front == Q.rear;//队头与队尾指向通一个地方时,队为空
}

判满

//队满判断(采用的时牺牲一个空间的方式来判断)
bool fullQueue(SeqQueue &Q) {
	return (Q.rear + 1) % MaxSize  == Q.front;
}

入队

//入队
bool enQueue(SeqQueue &Q, int e) {
	if (fullQueue(Q)) //栈满时,返回false
		return false;
	Q.data[Q.rear] = e;//在队尾插入元素
	Q.rear = (Q.rear + 1) % MaxSize; //计算队尾指针
	return true;
}

出队

//出队
int deQueue(SeqQueue &Q) {
	if (emptyQueue(Q)) //队空时,返回-1
		return -1;
	int e = Q.data[Q.front];//出队元素
	Q.front = (Q.front + 1) % MaxSize; //计算队头指针
	return e;
}

循环队列代码汇总

#include <stdio.h>
#include <stdlib.h>

//定义队列最大个数(采用循环队列时,需要牺牲一个空间来方便处理队满的情况)
#define MaxSize 11

//定义队列
typedef struct {
	int data[MaxSize];//存放队列数据
	int front, rear; //队头、队尾指针
} SeqQueue;

//初始化
void initQueue(SeqQueue &Q) {
	Q.front = 0;
	Q.rear = 0;
}

//队空判断
bool emptyQueue(SeqQueue &Q) {
	return Q.front == Q.rear;//队头与队尾指向通一个地方时,队为空
}

//队满判断(采用的时牺牲一个空间的方式来判断)
bool fullQueue(SeqQueue &Q) {
	return (Q.rear + 1) % MaxSize  == Q.front;
}

//入队
bool enQueue(SeqQueue &Q, int e) {
	if (fullQueue(Q)) //栈满时,返回false
		return false;
	Q.data[Q.rear] = e;//在队尾插入元素
	Q.rear = (Q.rear + 1) % MaxSize; //计算队尾指针
	return true;
}

//出队
int deQueue(SeqQueue &Q) {
	if (emptyQueue(Q)) //队空时,返回-1
		return -1;
	int e = Q.data[Q.front];//出队元素
	Q.front = (Q.front + 1) % MaxSize; //计算队头指针
	return e;
}

//显示队列元素
void showQueue(SeqQueue Q) {
	while (Q.front != Q.rear) {
		if (Q.front + 1 == Q.rear)
			printf("%d\n", Q.data[Q.front]);
		else
			printf("%d <- ", Q.data[Q.front]);
		Q.front = (Q.front + 1) % MaxSize;
	}
}

int main() {
	SeqQueue Q;
	initQueue(Q);
	printf("入队10个元素\n");
	for (int i = 1; i <= 10; i++) {
		enQueue(Q, i);
	}
	showQueue(Q);
	printf("出队5个元素\n");
	for (int i = 0; i < 5; i++) {
		deQueue(Q);
	}
	showQueue(Q);
	printf("再入队10个元素\n");
	for (int i = 1; i <= 10; i++) {
		enQueue(Q, i + 10);//队列满了后的元素不会入队
	}
	showQueue(Q);
	return 0;
}

链队

定义

//定义链队列节点
typedef struct LQNode {
	int data;//节点数据
	struct LQNode *next;//下一个节点指针
} LQNode;

//定义链队列
typedef struct {
	LQNode *front, *rear; //队头,队尾指针
} LinkQueue;

初始化

//初始化链队列
void initQueue(LinkQueue &LQ) {
	LQ.front = LQ.rear = (LQNode *)malloc(sizeof(LQNode));//队头与队尾指针都先指向同一个节点
	LQ.front->next = NULL;//队头的下一个节点指向空
}

判空

//判断队空
bool emptyQueue(LinkQueue &LQ) {
	return LQ.front == LQ.rear; //队头与队为都指向同一个节点即为空
}

入队

/**
 * @brief 入队(队尾入队)
 *
 * @param LQ 链队列
 * @param e 入队元素
 **/
void enQueue(LinkQueue &LQ, int e) {
	LQNode *s = (LQNode *)malloc(sizeof(LQNode));//给入队节点分配空间
	s->data = e;//赋值元素
	s->next = NULL;//新入队的节点的下一个节点为空
	LQ.rear->next = s;//将队尾指针的下一个节点指向新入队的元素
	LQ.rear = s;//修改队尾指针,指向新入队的元素
}

出队

/**
 * @brief 出队(队头出队)
 *
 * @param LQ 链队列
 * @param e 出队元素
 *
 * @return 出队是否成功
 **/
bool deQueue(LinkQueue &LQ, int &e) {
	if (emptyQueue(LQ)) //队列为空,返回false
		return false;
	LQNode *p = LQ.front->next;//指向队头节点
	e = p->data;//队头节点数据
	LQ.front->next = p->next;
	if (LQ.rear == p) //如果出队的是最后一个节点,就将队列置空
		LQ.front = LQ.rear;//若没有这一步,后面释放空间后,队尾指针就丢失了
	free(p);
	return true;
}

链队列代码汇总

#include <stdio.h>
#include <stdlib.h>

//定义链队列节点
typedef struct LQNode {
	int data;//节点数据
	struct LQNode *next;//下一个节点指针
} LQNode;

//定义链队列
typedef struct {
	LQNode *front, *rear; //队头,队尾指针
} LinkQueue;

//初始化链队列
void initQueue(LinkQueue &LQ) {
	LQ.front = LQ.rear = (LQNode *)malloc(sizeof(LQNode));//队头与队尾指针都先指向同一个节点
	LQ.front->next = NULL;//队头的下一个节点指向空
}

//判断队空
bool emptyQueue(LinkQueue &LQ) {
	return LQ.front == LQ.rear; //队头与队为都指向同一个节点即为空
}

/**
 * @brief 入队(队尾入队)
 *
 * @param LQ 链队列
 * @param e 入队元素
 **/
void enQueue(LinkQueue &LQ, int e) {
	LQNode *s = (LQNode *)malloc(sizeof(LQNode));//给入队节点分配空间
	s->data = e;//赋值元素
	s->next = NULL;//新入队的节点的下一个节点为空
	LQ.rear->next = s;//将队尾指针的下一个节点指向新入队的元素
	LQ.rear = s;//修改队尾指针,指向新入队的元素
}

/**
 * @brief 出队(队头出队)
 *
 * @param LQ 链队列
 * @param e 出队元素
 *
 * @return 出队是否成功
 **/
bool deQueue(LinkQueue &LQ, int &e) {
	if (emptyQueue(LQ)) //队列为空,返回false
		return false;
	LQNode *p = LQ.front->next;//指向队头节点
	e = p->data;//队头节点数据
	LQ.front->next = p->next;
	if (LQ.rear == p) //如果出队的是最后一个节点,就将队列置空
		LQ.front = LQ.rear;//若没有这一步,后面释放空间后,队尾指针就丢失了
	free(p);
	return true;
}

//显示队列元素
void  showQueue(LinkQueue LQ) {
	LQNode *p = LQ.front->next;
	while (p != NULL) {
		if (p->next != NULL)
			printf("%d <- ", p->data);
		else
			printf("%d\n", p->data);
		p = p->next;
	}
}

int main() {
	LinkQueue LQ;
	initQueue(LQ);
	for (int i = 1; i <= 10; i++) {
		enQueue(LQ, i);
	}
	printf("入队后节点信息为:");
	showQueue(LQ);
	int e;//记录出队元素
	printf("出队元素为:");
	for (int i = 1; i <= 5; i++) {
		deQueue(LQ, e);
		printf("%d ", e);
	}
	printf("\n");
	printf("剩余节点信息为:");
	showQueue(LQ);
	for (int i = 1; i <= 10; i++) {
		enQueue(LQ, i + 10);
	}
	printf("再入队后,结点信息为:");
	showQueue(LQ);
	return 0;
}

二叉树

定义

//定义二叉树
typedef struct BiNode {
	int data;//节点数据
	struct BiNode *lChild, *rChild;//左右孩子节点
} BiNode, *BiTree;

先序遍历(递归)

//先序遍历
void preOrder(BiTree T) {
	if (T != NULL) {
		visit(T);
		preOrder(T->lChild);
		preOrder(T->rChild);
	}
}

中序遍历(递归)

//中序遍历
void inOrder(BiTree T) {
	if (T != NULL) {
		inOrder(T->lChild);
		visit(T);
		inOrder(T->rChild);
	}
}

后序遍历(递归)

//后序遍历
void postOrder(BiTree T) {
	if (T != NULL) {
		postOrder(T->lChild);
		postOrder(T->rChild);
		visit(T);
	}
}

层序遍历


//层序遍历
void levelOrder(BiTree &T) {
	queue<BiNode *> Q;//初始化队列
	BiTree p;
	Q.push(T);//根结点入队
	while (!Q.empty()) {//队列不空时执行
		p = Q.front();//获取队头元素(c++库函数只能先获取到队头元素,然后调用pop出队)
		Q.pop();//队头元素出队
		visit(p);//访问结点
		if (p->lChild != NULL) {//当前结点有左子树时,左子树根结点入队
			Q.push(p->lChild);
		}
		if (p->rChild != NULL) {//当前结点有右子树时,右子树根结点入队
			Q.push(p->rChild);
		}
	}
}

中序遍历(非递归)

//中序遍历(非递归)
void inOrder2(BiTree &T) {
	stack<BiNode *> S;//初始化一个栈
	BiTree p = T;//p是遍历指针
	while (p != NULL || !S.empty()) {//p所指向的结点非空时或者栈不为空时循环
		if (p != NULL) {//当前结点非空时,把当前结点入栈,并将遍历指针p指向当前结点的左孩子
			S.push(p);
			p = p->lChild;//会一直把左还在入栈,直到没有做孩子为止
		} else {
			p = S.top();//获取栈顶元素(c++库函数只能先获取到栈顶元素,然后调用pop出栈)
			S.pop();//出栈
			visit(p);//访问当前结点
			p = p->rChild;//右孩子入栈
		}
	}
}

二叉树代码汇总

#include<iostream>
#include<stack>
#include<queue>
using namespace std;

#define Size 10 //节点个数

//定义二叉树
typedef struct BiNode {
	int data;//节点数据
	struct BiNode *lChild, *rChild;//左右孩子节点
} BiNode, *BiTree;

//初始化一棵二叉树
void initBiTree(BiTree &T) {
	T = (BiNode *) malloc(sizeof(BiNode));
	T->lChild = T->rChild = NULL; //左右孩子节点置空
}

//为二叉树添加一些测试数据(手动添加)
void  addTestData(BiTree &T) {
	T->data = 666;
	BiNode *b[Size];
	for (int i = 0; i < Size; i++) {
		b[i] = (BiNode*)malloc(sizeof(BiNode));
		b[i]->data = i;
	}
	T->lChild = b[0];
	T->rChild = b[1];
	int i = 0, j = 0;
	while (i < Size) {
		j = 2 * ( i + 1) ;
		if (j < Size) {
			b[i]->lChild = b[j];
			b[i]->rChild = b[j + 1];
		} else {
			b[i]->lChild = NULL;
			b[i]->rChild = NULL;
		}
		i++;
	}
}

//访问节点操作
void visit(BiTree T) {
	printf("%d ", T->data);
}

//先序遍历
void preOrder(BiTree T) {
	if (T != NULL) {
		visit(T);
		preOrder(T->lChild);
		preOrder(T->rChild);
	}
}

//中序遍历
void inOrder(BiTree T) {
	if (T != NULL) {
		inOrder(T->lChild);
		visit(T);
		inOrder(T->rChild);
	}
}

//后序遍历
void postOrder(BiTree T) {
	if (T != NULL) {
		postOrder(T->lChild);
		postOrder(T->rChild);
		visit(T);
	}
}
//中序遍历(非递归)
void inOrder2(BiTree &T) {
	stack<BiNode *> S;//初始化一个栈
	BiTree p = T;//p是遍历指针
	while (p != NULL || !S.empty()) {//p所指向的结点非空时或者栈不为空时循环
		if (p != NULL) {//当前结点非空时,把当前结点入栈,并将遍历指针p指向当前结点的左孩子
			S.push(p);
			p = p->lChild;//会一直把左还在入栈,直到没有做孩子为止
		} else {
			p = S.top();//获取栈顶元素(c++库函数只能先获取到栈顶元素,然后调用pop出栈)
			S.pop();//出栈
			visit(p);//访问当前结点
			p = p->rChild;//右孩子入栈
		}
	}
}

//层序遍历
void levelOrder(BiTree &T) {
	queue<BiNode *> Q;//初始化队列
	BiTree p;
	Q.push(T);//根结点入队
	while (!Q.empty()) {//队列不空时执行
		p = Q.front();//获取队头元素(c++库函数只能先获取到队头元素,然后调用pop出队)
		Q.pop();//队头元素出队
		visit(p);//访问结点
		if (p->lChild != NULL) {//当前结点有左子树时,左子树根结点入队
			Q.push(p->lChild);
		}
		if (p->rChild != NULL) {//当前结点有右子树时,右子树根结点入队
			Q.push(p->rChild);
		}
	}
}

int main() {
	BiTree T;
	initBiTree(T);
	addTestData(T);
//	preOrder(T);
//	inOrder(T);
//	inOrder2(T);
//	postOrder(T);
	levelOrder(T);
	return 0;
}

查找

定义顺序表

//多定义一个空间,将0号元素位置置空,作为哨兵使用
#define MaxSize 21

//定义查找表
typedef struct {
	int data[MaxSize];//数据
	int length;//表长度
} SSTable;

顺序查找

//顺序查找.找到返回元素所在位置,找不到时返回0
int seqSearch(SSTable ST, int key) {
	int i;//记录找到元素下标位置
	ST.data[0] = key;//哨兵
	for (i = ST.length; ST.data[i] != key; --i);//从后往前找。若没有找到,说明此时i已经为0了,返回的是哨兵位置
	return i;
}

折半查找(非递归)

//折半查找(非递归)(注意:折半查找需要元素有序)
int binSearch(SSTable ST, int key) {
	int low = 0, high = ST.length - 1, mid;
	while (low <= high) {
		mid = low + (high - low) / 2;//计算中间元素下标
		if (key == ST.data[mid])
			return mid;//中间位置正好就是待查找元素,直接返回
		else if (key < ST.data[mid]) {
			high = mid - 1;//从前半部分查找
		} else {
			low = mid + 1;//从后半部分查找
		}
	}
	return -1;//循环结束都没有返回,说明没有找到,返回-1
}

折半查找(递归)

//折半查找(递归)注意:折半查找需要元素有序)
int binSearchRec(SSTable ST, int key, int low, int high) {
	if (low > high) {//递归出口
		return -1;
	}
	int mid = low + (high - low) / 2;//计算中间元素下标
	if (key == ST.data[mid]) {
		return mid;//中间位置正好就是待查找元素,直接返回
	} else if (key < ST.data[mid]) {
		binSearchRec(ST, key, low, mid - 1);//从前半部分查找
	} else {
		binSearchRec(ST, key, mid + 1, high);//从后半部分查找
	}
}

查找代码汇总

#include <stdio.h>
#include <stdlib.h>

//多定义一个空间,将0号元素位置置空,作为哨兵使用
#define MaxSize 21

//定义查找表
typedef struct {
	int data[MaxSize];//数据
	int length;//表长度
} SSTable;

//初始化查找表
void initTable(SSTable &ST) {
	ST.length = 0;
}

//添加测试数据(用于顺序查找)
void addTestData1(SSTable &ST) {
	for (int i = 1; i <= 10; i++) {
		ST.data[i] = i;
		ST.length++;
	}
}

//添加测试数据(用于折半查找)
void addTestData2(SSTable &ST) {
	for (int i = 0; i < 10; i++) {
		ST.data[i] = i;
		ST.length++;
	}
}

//顺序查找.找到返回元素所在位置,找不到时返回0
int seqSearch(SSTable ST, int key) {
	int i;//记录找到元素下标位置
	ST.data[0] = key;//哨兵
	for (i = ST.length; ST.data[i] != key; --i);//从后往前找。若没有找到,说明此时i已经为0了,返回的是哨兵位置
	return i;
}

//折半查找(非递归)(注意:折半查找需要元素有序)
int binSearch(SSTable ST, int key) {
	int low = 0, high = ST.length - 1, mid;
	while (low <= high) {
		mid = low + (high - low) / 2;//计算中间元素下标
		if (key == ST.data[mid])
			return mid;//中间位置正好就是待查找元素,直接返回
		else if (key < ST.data[mid]) {
			high = mid - 1;//从前半部分查找
		} else {
			low = mid + 1;//从后半部分查找
		}
	}
	return -1;//循环结束都没有返回,说明没有找到,返回-1
}

//折半查找(递归)注意:折半查找需要元素有序)
int binSearchRec(SSTable ST, int key, int low, int high) {
	if (low > high) {//递归出口
		return -1;
	}
	int mid = low + (high - low) / 2;//计算中间元素下标
	if (key == ST.data[mid]) {
		return mid;//中间位置正好就是待查找元素,直接返回
	} else if (key < ST.data[mid]) {
		binSearchRec(ST, key, low, mid - 1);//从前半部分查找
	} else {
		binSearchRec(ST, key, mid + 1, high);//从后半部分查找
	}
}

int main() {
	SSTable ST;
	initTable(ST);
	addTestData1(ST);
	addTestData2(ST);
	for (int i = 1; i <= ST.length; i++) {
		printf("%d ", ST.data[i]);
	}
	printf("\n");
//	printf("顺序找到的元素位置为:%d\n", seqSearch(ST, 10));
	printf("折半找到的元素位置为:%d\n", binSearch(ST, 10));
	printf("折半找到的元素位置为:%d\n", binSearchRec(ST, 10, 0, ST.length - 1));
	return 0;
}

排序

插入排序

直接插入排序(不带哨兵)

/**
 * @brief 直接插入排序(不带哨兵)
 *
 * @param arr 待排序数组
 * @param arr 数组元素个数
 **/
void insertSort(int arr[], int n) {
	for (int i = 1; i < n; i++) {//从第二个元素开始
		if (arr[i] < arr[i - 1]) {//依次和前一个元素比较,若当前元素比前一个元素小,就说明需要移动元素
			int temp = arr[i];//记录下当前元素的数据
			int j;//记录元素最后应该插入的位置
			for (j = i; temp < arr[j - 1] && j > 0; j--) {//开始找正确的插入位置
				arr[j] = arr[j - 1];//比当前元素小的都将往后移
			}
			arr[j] = temp;//循环结束后,j所指的位置就是当前元素应该插入的位置
		}
	}
}

直接插入排序(带哨兵,第一个元素不会参与排序)

/**
 * @brief 直接插入排序(带哨兵,第一个元素不会参与排序)
 *
 * @param arr 待排序数组
 * @param arr 数组元素个数
 **/
void insertSort2(int arr[], int n) {
	int i, j;
	for (i = 2; i < n; i++) {
		if (arr[i] < arr[i - 1]) {
			arr[0] = arr[i]; //用第一个元素位置存放比较的元素
			for (j = i - 1; arr[0] < arr[j]; j--) {//开始找正确的插入位置
				arr[j + 1] = arr[j];//比当前元素小的都将往后移
			}
			arr[j + 1] = arr[0];//循环结束后,j所指的后一个位置就是当前元素应该插入的位置
		}
	}
}

折半插入排序

/**
 * @brief 折半插入排序(带哨兵,第一个位置元素,不会排序)
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void insertSort3(int arr[], int n) {
	int i, j, low, high, mid;
	for ( i = 2; i < n; i++) { //从第二个元素开始
		if (arr[i] < arr[i - 1]) {
			arr[0] = arr[i];//记录下当前元素到哨兵位置
			low = 1;
			high = i - 1;
			while (low <= high) {
				mid = low + (high - low) / 2;
				if (arr[0] < arr[mid]) { //查左边
					high = mid - 1;
				} else { //查右边
					low = mid + 1;
				}
			}
			//循环退出后,一定是low比high大一,而low所指向的位置恰好就是当前元素应该插入的位置
			//开始移动位置
			for (j = i - 1; j >= low; j--) {
				arr[j + 1] = arr[j];
			}
			arr[low] = arr[0];//将当前元素插入正确的位置
		}
	}
}

希尔排序


/**
 * @brief 希尔排序
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void shellSort(int arr[], int n) {
	int i, j, dk, temp; //dk为步长,temp用来记录临时值
	for (dk = n / 2; dk >= 1; dk /= 2) { //步长变化(当dk == 1的时候,希尔排序就变为了直接插入排序)
		for (i = dk; i < n; i++) {
			if (arr[i] < arr[i - dk]) {//条件满足时,说明后面的元素比前面的小,需要将当前元素插入到正确的位置
				temp = arr[i];//记录当前元素
				for (j = i - dk; j >= 0 && temp < arr[j]; j -= dk) {
					arr[j + dk] = arr[j];//移动元素
				}
				arr[j + dk] = temp;//插入到正确位置
			}
		}
	}
}

冒泡排序

/**
 * @brief 冒泡排序
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void bubbleSort(int arr[], int n) {
	bool flag;//记录当前是否发生交换
	int temp;//辅助交换用的临时变量
	for (int i = 0; i < n - 1; i++) {//控制循环次数
		flag = false;//本次循环开始时,修改交换标记为false
		for (int j = n - 1; j > i; j--) {
			if (arr[j] < arr[j - 1]) { //小元素往前冒
				temp = arr[j];
				arr[j ] = arr[j - 1];
				arr[j - 1] = temp;
				flag = true;//发生交换后,修改交换标记为true
			}
		}
		if (!flag) //本次循环没有发生交换,说明已排好序,退出循环
			break;
	}
}

快速排序

/**
 * @brief 快排的划分操作
 *
 * @param arr 待排序数组
 * @param low 待排序区域第一个元素下标
 * @param high 待排序区域最后一个元素下标
 *
 * @return 放枢轴元素的位置
 **/
int partition(int arr[], int low, int high) {
	int pivot = arr[low];//将第一个元素作为枢轴值
	while (low < high) { //跳出循环条件
		while (low < high && arr[high] >= pivot) {//从右往左开始,找到第一个比枢轴值小的元素
			high--;//循环满足,说明当前元素比枢轴值大,此时需要继续往左找
		}
		arr[low] = arr[high];//将比枢轴值小的元素移动到左端
		while (low < high && arr[low] <= pivot) {//从左往右开始,找到第一个比枢轴值大的元素
			low++;//循环满足,说明当前元素比枢轴值小,此时需要继续往右找
		}
		arr[high] = arr[low];//将比枢轴值大的元素移动到右端
	}
	arr[low] = pivot;//枢轴元素存放到最终位置
	return low;//返回存放枢轴元素的位置
}

/**
 * @brief 快速排序
 *
 * @param arr 待排序数组
 * @param low 待排序区域第一个元素下标
 * @param high 待排序区域最后一个元素下标
 **/
void quickSort(int arr[], int low, int high) {
	if (low < high) { //递归出口(low >= high时,划分结束,排序完成)
		int pivot = partition(arr, low, high); //划分
		quickSort(arr, low, pivot - 1); //前部分继续排序
		quickSort(arr, pivot + 1, high); //前部分继续排序
	}
}

简单选择排序

/**
 * @brief 选择排序
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void selectSort(int arr[], int n) {
	int min, temp;//min记录最小元素的下标,temp交换的临时变量
	for (int i = 0; i < n; i++) {
		min = i;//记录最小元素下标
		for (int j = i + 1; j < n; j++) {//往后找,比当前元素小的元素下标
			if (arr[min] > arr[j]) {
				min = j;//更新最小元素的下标
			}
		}
		if (min != i) {//后面有比当前元素小的元素才交换
			//将找到的最小元素交换到前面
			temp = arr[i];
			arr[i] = arr[min];
			arr[min] = temp;
		}
	}
}

堆排序

/**
 * @brief 将以k为根的子树调整为大根堆
 *
 * @param arr 待排序数组
 * @param k 待调整元素下标
 * @param n 数组元素个数
 **/
void adjustDown(int arr[], int k, int n) {
	arr[0] = arr[k];//数组第一个位置用来暂存当前待调整元素
	for (int i = 2 * k; i <= n; i *= 2) {// 2 * k表示k的左孩子
		if (i < n && arr[i] < arr[i + 1])//i < n说明存在右孩子,若左孩子小于右孩子,i记录的最大值更新为右孩子
			i++;
		if (arr[0] >= arr[i])//如果当前元素比孩子节点最大值大,就不用调整,直接break
			break;
		else {//需要调整
			arr[k] = arr[i];//k所指节点的数据更新为孩子中较大的一个值,更新后,可能造成原本是大根堆的树,不满足大根堆条件,需要再次判断
			k = i;//将原来待调整的节点k,更新为判断i这个节点是否符合大根堆条件。该过程可以看作小元素下坠。
		}
	}
	arr[k] = arr[0];//最后,将原本k所指的数据(在0号位置备份的数据),放到新的k所指位置。
}


/**
 * @brief 创建大根堆
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void buildMaxHeap(int arr[], int n) {
	for (int i = n / 2; i > 0; i--) {//从最后一个非叶子结点往前检查,依次调整为大根堆
		adjustDown(arr, i, n);//检查当前节点,并将该节点调整为大根堆
	}
}

/**
 * @brief 堆排序
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void heapSort(int arr[], int n) {
	buildMaxHeap(arr, n);//构建大根堆
	for (int i = n; i > 1; i--) {//从最后一个位置开始,依次将每一个堆顶元素交换到堆底,交换后的元素再次调整为大根堆
		swap(arr[i], arr[1]);
		adjustDown(arr, 1, i - 1);
	}
}

归并排序

/**
 * @brief 归并过程
 *
 * @param arr 待排序数组
 * @param low 待排序序列的第一个元素下标
 * @param mid 排序序列的中间元素下标
 * @param high 排序序列的最后一个元素下标
 **/
void merge(int arr[], int low, int mid, int high) {
	int res[high - low + 1];//辅助数组(辅助数组大小通过计算获得)
	int k = 0;//记录辅助数组起始下标
	int i = low;//记录左边部分起始下标
	int j = mid + 1;//记录右边部分起始下标
	while (i <= mid && j <= high) {//左右部分均还有元素时,比较两个部分的大小
		if (arr[i] <= arr[j]) {//如果左边的小或者两者相等,就将左边的数据放到辅助数组中
			res[k++] = arr[i++];//赋值,并修改辅助数组以及左边部分的下标
		} else {//如果右边的小,就将右边的数据放到辅助数组中
			res[k++] = arr[j++];//赋值,并修改辅助数组以及右边部分的下标
		}
	}
	//上面while循环结束后,i,j中必有一个走到了末尾,则下面的两个while循环,仅会执行一个
	while (i <= mid)//左边部分还有剩余,将左边剩余的全部复制到辅助数组中
		res[k++] = arr[i++];
	while (j <= high)//右边部分还有剩余,将右边剩余的全部复制到辅助数组中
		res[k++] = arr[j++];
	for (i = low, j = 0 ; j < k; i++, j++)//将辅助数组中的元素复制到原来数组中
		arr[i] = res[j];
}

/**
 * @brief 归并排序
 *
 * @param arr 待排序数组
 * @param low 待排序序列的第一个元素下标
 * @param high 排序序列的最后一个元素下标
 **/
void mergeSort(int arr[], int low, int high) {
	if (low < high) {
		int mid = low + (high - low) / 2;
		mergeSort(arr, low, mid);
		mergeSort(arr, mid + 1, high);
		merge(arr, low, mid, high);
	}
}

排序代码汇总

#include <stdio.h>
#include <stdlib.h>

//交换两个元素
void swap(int &x, int &y) {
	int temp = x;
	x = y;
	y = temp;
}

/**
 * @brief 直接插入排序(不带哨兵)
 *
 * @param arr 待排序数组
 * @param arr 数组元素个数
 **/
void insertSort(int arr[], int n) {
	for (int i = 1; i < n; i++) {//从第二个元素开始
		if (arr[i] < arr[i - 1]) {//依次和前一个元素比较,若当前元素比前一个元素小,就说明需要移动元素
			int temp = arr[i];//记录下当前元素的数据
			int j;//记录元素最后应该插入的位置
			for (j = i; temp < arr[j - 1] && j > 0; j--) {//开始找正确的插入位置
				arr[j] = arr[j - 1];//比当前元素小的都将往后移
			}
			arr[j] = temp;//循环结束后,j所指的位置就是当前元素应该插入的位置
		}
	}
}

/**
 * @brief 直接插入排序(带哨兵,第一个位置元素不会参与排序)
 *
 * @param arr 待排序数组
 * @param arr 数组元素个数
 **/
void insertSort2(int arr[], int n) {
	int i, j;
	for (i = 2; i < n; i++) {//从第二个元素开始
		if (arr[i] < arr[i - 1]) {
			arr[0] = arr[i]; //用第一个元素位置存放比较的元素
			for (j = i - 1; arr[0] < arr[j]; j--) {//开始找正确的插入位置
				arr[j + 1] = arr[j];//比当前元素小的都将往后移
			}
			arr[j + 1] = arr[0];//循环结束后,j所指的后一个位置就是当前元素应该插入的位置
		}
	}
}

/**
 * @brief 折半插入排序(带哨兵,第一个位置元素,不会排序)
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void insertSort3(int arr[], int n) {
	int i, j, low, high, mid;
	for ( i = 2; i < n; i++) { //从第二个元素开始
		if (arr[i] < arr[i - 1]) {
			arr[0] = arr[i];//记录下当前元素到哨兵位置
			low = 1;
			high = i - 1;
			while (low <= high) {
				mid = low + (high - low) / 2;
				if (arr[0] < arr[mid]) { //查左边
					high = mid - 1;
				} else { //查右边
					low = mid + 1;
				}
			}
			//循环退出后,一定是low比high大一,而low所指向的位置恰好就是当前元素应该插入的位置
			//开始移动位置
			for (j = i - 1; j >= low; j--) {
				arr[j + 1] = arr[j];
			}
			arr[low] = arr[0];//将当前元素插入正确的位置

		}
	}
}

/**
 * @brief 希尔排序
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void shellSort(int arr[], int n) {
	int i, j, dk, temp; //dk为步长,temp用来记录临时值
	for (dk = n / 2; dk >= 1; dk /= 2) { //步长变化(当dk == 1的时候,希尔排序就变为了直接插入排序)
		for (i = dk; i < n; i++) {
			if (arr[i] < arr[i - dk]) {//条件满足时,说明后面的元素比前面的小,需要将当前元素插入到正确的位置
				temp = arr[i];//记录当前元素
				for (j = i - dk; j >= 0 && temp < arr[j]; j -= dk) {
					arr[j + dk] = arr[j];//移动元素
				}
				arr[j + dk] = temp;//插入到正确位置
			}
		}
	}
}

/**
 * @brief 希尔排序
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void shellSort2(int arr[], int n) {
	int i, j, dk; //dk为步长
	for (dk = n / 2; dk >= 1; dk /= 2) { //步长变化
		for (i = dk + 1; i < n; i++) {
			if (arr[i] < arr[i - dk]) {
				arr[0] = arr[i];//暂存在arr[0]
				for (j = i - dk; j > 0 && arr[0] < arr[j]; j -= dk) {
					arr[j + dk] = arr[j];
				}
				arr[j + dk] = arr[0];
			}
		}
	}
}

/**
 * @brief 冒泡排序
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void bubbleSort(int arr[], int n) {
	bool flag;//记录当前是否发生交换
	for (int i = 0; i < n - 1; i++) {//控制循环次数
		flag = false;//本次循环开始时,修改交换标记为false
		for (int j = n - 1; j > i; j--) {
			if (arr[j] < arr[j - 1]) { //小元素往前冒
				swap(arr[j], arr[j - 1]);
				flag = true;//发生交换后,修改交换标记为true
			}
		}
		if (!flag) //本次循环没有发生交换,说明已排好序,退出循环
			break;
	}
}

/**
 * @brief 快排的划分操作
 *
 * @param arr 待排序数组
 * @param low 待排序区域第一个元素下标
 * @param high 待排序区域最后一个元素下标
 *
 * @return 返回枢轴元素的位置
 **/
int partition(int arr[], int low, int high) {
	int pivot = arr[low];//将第一个元素作为枢轴值
	while (low < high) { //跳出循环条件
		while (low < high && arr[high] >= pivot) {//从右往左开始,找到第一个比枢轴值小的元素
			high--;//循环满足,说明当前元素比枢轴值大,此时需要继续往左找
		}
		arr[low] = arr[high];//将比枢轴值小的元素移动到左端
		while (low < high && arr[low] <= pivot) {//从左往右开始,找到第一个比枢轴值大的元素
			low++;//循环满足,说明当前元素比枢轴值小,此时需要继续往右找
		}
		arr[high] = arr[low];//将比枢轴值大的元素移动到右端
	}
	arr[low] = pivot;//枢轴元素存放到最终位置
	return low;//返回存放枢轴元素的位置
}

/**
 * @brief 快速排序
 *
 * @param arr 待排序数组
 * @param low 待排序区域第一个元素下标
 * @param high 待排序区域最后一个元素下标
 **/
void quickSort(int arr[], int low, int high) {
	if (low < high) { //递归出口(low >= high时,划分结束,排序完成)
		int pivot = partition(arr, low, high); //划分
		quickSort(arr, low, pivot - 1); //前部分继续排序
		quickSort(arr, pivot + 1, high); //前部分继续排序
	}
}


/**
 * @brief 选择排序
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void selectSort(int arr[], int n) {
	int min;//min记录最小元素的下标
	for (int i = 0; i < n; i++) {
		min = i;//记录最小元素下标
		for (int j = i + 1; j < n; j++) {//往后找,比当前元素小的元素下标
			if (arr[min] > arr[j]) {
				min = j;//更新最小元素的下标
			}
		}
		if (min != i) {//后面有比当前元素小的元素才交换
			//将找到的最小元素交换到前面
			swap(arr[i], arr[min]);
		}
	}
}

/**
 * @brief 将待调整元素k向上调整(主要用于删除堆元素后)
 *
 * @param arr 待排序数组
 * @param k 待调整元素下标
 **/
void adjustUp(int arr[], int k) {
	arr[0] = arr[k];//数组第一个位置用来暂存当前待调整元素
	int i = k / 2;
	while (i > 0 && arr[i] < arr[0]) {
		arr[k] = arr[i];
		k = i;
		i = k / 2;
	}
	arr[k] = arr[0];
}

/**
 * @brief 将以k为根的子树调整为大根堆
 *
 * @param arr 待排序数组
 * @param k 待调整元素下标
 * @param n 数组元素个数
 **/
void adjustDown(int arr[], int k, int n) {
	arr[0] = arr[k];//数组第一个位置用来暂存当前待调整元素
	for (int i = 2 * k; i <= n; i *= 2) {// 2 * k表示k的左孩子
		if (i < n && arr[i] < arr[i + 1])//i < n说明存在右孩子,若左孩子小于右孩子,i记录的最大值更新为右孩子
			i++;
		if (arr[0] >= arr[i])//如果当前元素比孩子节点最大值大,就不用调整,直接break
			break;
		else {//需要调整
			arr[k] = arr[i];//k所指节点的数据更新为孩子中较大的一个值,更新后,可能造成原本是大根堆的树,不满足大根堆条件,需要再次判断
			k = i;//将原来待调整的节点k,更新为判断i这个节点是否符合大根堆条件。该过程可以看作小元素下坠。
		}
	}
	arr[k] = arr[0];//最后,将原本k所指的数据(在0号位置备份的数据),放到新的k所指位置。
}


/**
 * @brief 创建大根堆
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void buildMaxHeap(int arr[], int n) {
	for (int i = n / 2; i > 0; i--) {//从最后一个非叶子结点往前检查,依次调整为大根堆
		adjustDown(arr, i, n);//检查当前节点,并将该节点调整为大根堆
	}
}

/**
 * @brief 堆排序
 *
 * @param arr 待排序数组
 * @param n 数组元素个数
 **/
void heapSort(int arr[], int n) {
	buildMaxHeap(arr, n);//构建大根堆
	for (int i = n; i > 1; i--) {//从最后一个位置开始,依次将每一个堆顶元素交换到堆底,交换后的元素再次调整为大根堆
		swap(arr[i], arr[1]);
		adjustDown(arr, 1, i - 1);
	}
}
//
///**
// * @brief 归并过程
// *
// * @param arr 待排序数组
// * @param low 待排序序列的第一个元素下标
// * @param mid 排序序列的中间元素下标
// * @param high 排序序列的最后一个元素下标
// **/
//void merge(int arr[], int low, int mid, int high) {
//	int i, j, k;//记录下标变量
//	int temp[high - low + 1];//辅助数组
//	for (k = low; k <= high; k++)//将待排序数组,复制一份到辅助数组
//		temp[k] = arr[k];
//	for (i = low, j = mid + 1, k = i; i <= mid && j <= high; k++) {
//		if (temp[i] <= temp [j]) //比较辅助数组左右两段中的元素
//			arr[k]  = temp[i++];
//		else
//			arr[k]  = temp[j++];
//	}
//	while (i <= mid)
//		arr[k++] = temp[i++];
//	while (j <= high)
//		arr[k++] = temp[j++];
//}
//
///**
// * @brief 归并排序
// *
// * @param arr 待排序数组
// * @param low 待排序序列的第一个元素下标
// * @param high 排序序列的最后一个元素下标
// **/
//void mergeSort(int arr[], int low, int high) {
//	if (low < high) {
//		int mid = low + (high - low) / 2;//从中间划分两个子序列
//		mergeSort(arr, low, mid); //对左边部分进行归并排序
//		mergeSort(arr, mid + 1, high); //对右边部分进行归并排序
//		merge(arr, low, mid, high); //归并操作
//	}
//}


/**
 * @brief 归并过程
 *
 * @param arr 待排序数组
 * @param low 待排序序列的第一个元素下标
 * @param mid 排序序列的中间元素下标
 * @param high 排序序列的最后一个元素下标
 **/
void merge(int arr[], int low, int mid, int high) {
	int res[high - low + 1];//辅助数组(辅助数组大小通过计算获得)
	int k = 0;//记录辅助数组起始下标
	int i = low;//记录左边部分起始下标
	int j = mid + 1;//记录右边部分起始下标
	while (i <= mid && j <= high) {//左右部分均还有元素时,比较两个部分的大小
		if (arr[i] <= arr[j]) {//如果左边的小或者两者相等,就将左边的数据放到辅助数组中
			res[k++] = arr[i++];//赋值,并修改辅助数组以及左边部分的下标
		} else {//如果右边的小,就将右边的数据放到辅助数组中
			res[k++] = arr[j++];//赋值,并修改辅助数组以及右边部分的下标
		}
	}
	//上面while循环结束后,i,j中必有一个走到了末尾,则下面的两个while循环,仅会执行一个
	while (i <= mid)//左边部分还有剩余,将左边剩余的全部复制到辅助数组中
		res[k++] = arr[i++];
	while (j <= high)//右边部分还有剩余,将右边剩余的全部复制到辅助数组中
		res[k++] = arr[j++];
	for (i = low, j = 0 ; j < k; i++, j++)//将辅助数组中的元素复制到原来数组中
		arr[i] = res[j];
}

/**
 * @brief 归并排序
 *
 * @param arr 待排序数组
 * @param low 待排序序列的第一个元素下标
 * @param high 排序序列的最后一个元素下标
 **/
void mergeSort(int arr[], int low, int high) {
	if (low < high) {
		int mid = low + (high - low) / 2;
		mergeSort(arr, low, mid);
		mergeSort(arr, mid + 1, high);
		merge(arr, low, mid, high);
	}
}



//显示排序后的数据
void show(int arr[], int n, bool flag = false) {
	//flag为true时,说明第一个元素是哨兵,遍历时,从下标为1开始。
	for (int i = flag ? 1 : 0; i < n; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main() {
	int arr[] = { 5, 4, 2, 3, 8, 1, 6, 7, 9, 10, 15, 14, 12, 11, 13};
	int n = sizeof(arr) / sizeof(int);//计算数组元素个数
	//插入排序
//	insertSort(arr, n);
//	printf("不带哨兵插入排序后\n");
//	show(arr, n);
//	insertSort2(arr, n);
//	printf("带哨兵插入排序后\n");
//	show(arr, n, true);
//	insertSort3(arr, n);
//	printf("带哨兵折半插入排序后\n");
//	show(arr, n, true);
//	shellSort(arr, n);
//	printf("希尔排序后\n");
//	show(arr, n);
//	bubbleSort(arr, n);
//	printf("冒泡排序后\n");
//	show(arr, n);
//	quickSort(arr, 0, n - 1);
//	printf("快速排序后\n");
//	show(arr, n);
//	selectSort(arr, n);
//	printf("简单选择排序后\n");
//	show(arr, n);
//	heapSort(arr, n - 1);
//	printf("堆排序后\n");
//	show(arr, n, true);
	mergeSort(arr, 0, n - 1);
	printf("归并排序后\n");
	show(arr, n);
	return 0;
}
  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值