数据结构—查找算法总述

1:最简单的查找方法,只要数据在顺序表中存储就可以使用逐个遍历的方法进行查找:查找成功返回关键字在顺序表中的位置,查找是失败返回-1.

int Sequential_Search(int arr[], int key, int len){
	int i = 0;
	for (i = 0; i < len; i++){
		if (arr[i] == key){
			return i;
		}
	}
	return -1;
}

2:当顺序表中的数字有序排列时我们可以使用折半查找的方法来减少查找次数:查找成功返回关键字在顺序表中的位置,查找是失败返回-1.

//有序表查找-二分查找
int Binary_Search(int arr[], int key, int len){
	int left = 0;
	int right = len - 1;
	int quit = 0;
	while (!quit){
		int mid = (left + right) / 2;
		if (key == arr[mid]){
			return mid;
		}
		if (key < arr[mid]){
			right = mid - 1;
		}
		if (key>arr[mid]){
			left = mid + 1;
		}
	}
	return -1;
}

3:在折半查找的基础上在进行优化,计算每次查找位置mid的计算不仅仅取决于left和right还取决于数值的大小比例,即插值查找可减少查找次数。

//有序查找-插值查找
int Insert_Search(int arr[], int key, int len){
	int left = 0;
	int right = len - 1;
	int quit = 0;
	while (!quit){
		int mid = left + ((key - arr[left]) / (arr[right] - arr[left]))*(arr[right] - arr[left]);
		if (key == arr[mid]){
			return mid;
		}
		if (key < arr[mid]){
			right = mid - 1;
		}
		if (key>arr[mid]){
			left = mid + 1;
		}
	}
	return -1;
}

4:斐波那契查找:构造一个辅助斐波那契数组在帮助更快查找,不详述。

5:利用二叉树的思想将数存储在树中,左子树的数均比根结点小,右子树的数均比根结点大(类似于折半查找,但折半查找是静态的,而二叉排序树查找是动态的)。构建这样一个二叉排序树,每次查找仅需要比较关键字与当前结点数值的大小,比结点数值大即进入结点的右子树继续查找,比结点数值小即进入结点的左子树继续查找。

5.1:二叉排序树的数据结构

//二叉排序树
typedef struct BiTNode{
	int data;
	struct BiTNode *left;
	struct BiTNode *right;
}BitNode,*BiTree;

5.2:查找,递归实现

void Search_Tree(BiTree t, int key){
	if (key == t->data){
		printf("成功找到:%d\n",t->data);
	}
	else if (key < t->data){
		Search_Tree(t->left, key);
	}
	else{
		Search_Tree(t->right, key);
	}
}

5.3:删除

void Delet(BiTree *m){
	BiTree s, q;
	if ((*m)->right == NULL){
		q = *m;
		*m = (*m)->left;
		free(q);
	}
	else if ((*m)->left == NULL){
		q = *m;
		*m = (*m)->right;
		free(q);
	}
	else{
		q = *m;
		s = (*m)->left;
		while (s->right){
			q = s;
			s = s->right;
		}
		(*m)->data = s->data;
		if (q != *m){
			q->right = s->left;
		}
		else{
			q->left = s->left;
		}
		free(s);
	}
}
BiTree Delete_Tree(BiTree t, int key){
	BiTree bt;
	bt = t;
	if (bt == NULL){
		printf("树空,无法操作!\n");
	}
	else {
		if (key == bt->data){
			Delet(&bt);
			printf("已删除!\n");
		}
		else if (key < bt->data){
			Delete_Tree(bt->left, key);
		}
		else{
			Delete_Tree(bt->right, key);
		}
	}
	return t;
}

5.4构建平衡二叉树

//创建二叉排序
void Create_Tree(BiTree *t){
	int key;
	*t = NULL;
	printf("请输入数字:\n");
	scanf("%d", &key);
	while (key != -1){
		Insert_Tree(t, key);
		printf("请继续输入:\n");
		scanf("%d", &key);
	}
}

6:由于输入的数字可能就是有序的,这样构建的二叉排序树只有左子树或者只有右子树查找效率较低,所以在构建二叉树时需要构建平衡二叉树(每一个结点的左子树和右子树的高度差至多为1),平衡二叉树的查找性能更好。

6.1:平衡二叉树的数据结构

#define LH +1//左高
#define EH 0//等高
#define RH -1 //右高
//平衡二叉树
typedef struct BiTree{
	int data;
	int bf;
	struct BiTree *lchild;
	struct BiTree *rchild;
}BiNode,*BiNode_t;

6.2平衡二叉树的构建:

//平衡二叉树_右旋
void R_Rotate(BiNode_t *bp){
	BiNode_t l;
	l = (*bp)->lchild;
	(*bp)->lchild = l->rchild;
	l->rchild = (*bp);
	*bp = l;
}
//平衡二叉树_左旋
void L_Rotate(BiNode_t *bp){
	BiNode_t r;
	r = (*bp)->rchild;
	(*bp)->rchild = r->lchild;
	r->lchild = (*bp);
	(*bp) = r;
}
//左平衡旋转处理
void LeftBanlance(BiNode_t *bt){//进入该函数时即根结点已经不平衡是由左子树较高引起
	BiNode_t l, lr;
	l = (*bt)->lchild;
	switch (l->bf){
	case LH://左子树的bf与根结点同向
		(*bt)->bf = l->bf = EH;
		R_Rotate(bt);//只需要对根结点右旋即可
		break;
	case RH://左子树与根结点的bf相反,需对该右子树的左子树继续判断,进行两次旋转才能平衡
		lr = l->rchild;
		switch(lr->bf){
		case LH:
			(*bt)->bf = RH;
			l->bf = EH;
			break;
		case EH:
			(*bt)->bf = l->bf = EH;
			break;
		case RH:
			(*bt)->bf = EH;
			l->bf = LH;
			break;
		}
		lr->bf = EH;
		L_Rotate(&(*bt)->lchild);//先对根结点的右子树进行左旋
		R_Rotate(bt);//再对根结点进行右旋
	}
}
//右平衡旋转处理
void RightBanlance(BiNode_t *bt){//进入该函数时即根结点已经不平衡是由右子树较高引起
	BiNode_t r, rl;
	r = (*bt)->rchild;
	switch (r->bf){
	case LH://右子树与根结点的bf相反,需对该右子树的左子树继续判断,进行两次旋转才能平衡
		rl =r->lchild;
		switch (rl->bf){
		case LH:
			(*bt)->bf = EH;
			r->bf = RH;
			break;
		case EH:
			(*bt)->bf = r->bf = EH;
			break;
		case RH:
			(*bt)->bf = LH;
			r->bf = EH;
			break;
		}
		rl->bf = EH;
		R_Rotate(&(*bt)->rchild);//先对根结点的右子树进行右旋
		L_Rotate(bt);//再对根结点进行左旋
 		break;
	case RH://右子树的bf与根结点同向
		(*bt)->bf = r->bf = EH;
		L_Rotate(bt);//只需要对根结点左旋即可
		break;
	}
}
//平衡二叉树_插入
int InsertAVL(BiNode_t *bt, int e, int *taller){//taller反映是否插入数据,树是否长高,若为1则表示插入长高,若为0则表示未插入。
	if (!*bt){//为当前新结点申请空间,树长高,置taller为1
		*bt = (BiNode_t)malloc(sizeof(BiNode));
		(*bt)->data = e;
		(*bt)->lchild = (*bt)->rchild = NULL;
		(*bt)->bf = EH;
		*taller = 1;
	}
	else{
		if (e == (*bt)->data){//树中已存在和e相同关键字的结点则不再插入
			*taller = 0;
			return 0;
		}
		if (e < (*bt)->data){//在当前树的左子树中继续寻找
			if (!InsertAVL(&(*bt)->lchild, e, taller)){//未插入
				return 0;
			}
			if (*taller){//已插入左子树中且左子树长高
				switch ((*bt)->bf){
				case LH://原本左子树比右子树高,现插入左子树后需要左平衡处理
					LeftBanlance(bt);
					*taller = 0;
					break;
				case EH://原本左右子树等高,现插入左子树后树长高
					(*bt)->bf = LH;
					*taller = 1;
					break;
				case RH://原本右子树比左子树高,现插入左子树后树等高
					(*bt)->bf = EH;
					*taller = 0;
					break;
				}
			}
		}
		else{
			if (!InsertAVL(&(*bt)->rchild, e, taller)){//未插入
				return 0;
			}
			if (*taller){//已插入右子树中且右子树长高
				switch ((*bt)->bf){
				case LH://原本左子树比右子树高,现插入右子树后树等高
					(*bt)->bf = EH;
					*taller = 0;
					break;
				case EH://原本左右子树等高,现插入右子树后树长高
					(*bt)->bf = RH;
					*taller = 1;
					break;
				case RH://原本右子树比左子树高,现插入右子树,需要做右平衡处理
					RightBanlance(bt);
					*taller = 0;
					break;
				}
			}
		}
	}
	return 1;
}
//平衡二叉树_构建
void Create_BalanceTree(BiNode_t *bt){
	int i;
	int arr[] = { 12, 34, 65, 10, 3, 54, 6, 70, 4, 21 };
	int taller;
	int len = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < len; i++){
		InsertAVL(bt, arr[i], &taller);//对数组中的每个元素调用插入函数依次插入二叉树。
	}
}

6.3,构建好平衡二叉树后,使用递归的思想即可查找关键字,查找速度更快

7:哈希查找,及散列查找,已在其他文章中叙述,可查看。

你可能感兴趣的:(C-数据结构与算法)