四、查找与排序(下)(2)

树、二叉树简介

在这里插入图片描述
如果知道父节点,则子节点的下标为2i+1,2i+2
如果知道子节点,则父节点的下标为(i-1)/2

二叉树遍历方法:
先序(根 左右)、中序(左 根 右)、后序(左右 根)
先序:78 56 43 2 23 4 34 1 15
中序:2 43 23 56 4 78 1 34 15
后序:2 23 43 4 56 1 15 34 78

#include<bits/stdc++.h>
using namespace std;
template <class T>
int len(T& arr){
	return sizeof(arr)/sizeof(arr[0]);
}
class TreeAndArray{
	public:void preOrder(vector<int> vec,int i){
		if(i>=vec.size())
			return;
		cout<<vec[i]<<endl;
		preOrder(vec,i*2+1);
		preOrder(vec,i*2+2);
	}
	public:void inOrder(vector<int> vec,int i){
		if(i>=vec.size())
			return;
		inOrder(vec,i*2+1);
		cout<<vec[i]<<endl;
		inOrder(vec,i*2+2);
	}
	public:void backOrder(vector<int> vec,int i){
		if(i>=vec.size())
			return;
		backOrder(vec,i*2+1);
		backOrder(vec,i*2+2);
		cout<<vec[i]<<endl;
	}
}; 
int main()
{
	int arr[]={78,56,34,43,4,1,15,2,23};
	vector<int> vec(arr,arr+len(arr));
	TreeAndArray tree;
	tree.preOrder(vec,0);
	cout<<"==========="<<endl;
	tree.inOrder(vec,0);
	cout<<"==========="<<endl;
	tree.backOrder(vec,0);
	return 0;
}

堆的概念

  • 二叉堆是完全二叉树或者是近似完全二叉树
  • 二叉堆满足两个特性:
    • 1.父节点的键值总是大于或等于(小于或等于)任何一个 子节点的键值
    • 2.每个节点左子树和右子树都是一个二叉堆(都是最大堆或最小堆)
  • 任意节点的值都大于其他子节点的值–大顶堆
  • 任意节点的值都小于其他子节点的值–小顶堆

将数组堆化伪代码:(小顶堆)

MinHelp(A){
	n=A.length;
	for i from n/2-1down to 0{
		MinHeapFixDown(A,i,n);
	}
}
MinHeapFixDown(A,i,n){
	//找到左右孩子
	left = 2*i+1;
	right = 2*i+2;
	//考虑越界问题
	if(left>=n){
		return;
	}
	min = left;
	if(right>=n){
		min=left;
	}else{//左右孩子都没越界
		if(A[right]<A[left]){
			min = right;
		}
	}
	//min指向了左右孩子中较小的那个
	
	//如果A[i]比两个孩子都要小,不用调整
    if(A[i]<=A[min]){
    	return;
    }
	//否则,找到两个孩子中较小的,和i交换
	swap(A[i],A[min]);
	
	//小孩子那个位置的值发生了变化,i变更为小孩子那个位置,递归调整
	MinHeapFixDown(A,min,n);
}

完整代码

#include<bits/stdc++.h>
using namespace std;
template <class T>
int len(T& arr){
	return sizeof(arr)/sizeof(arr[0]);
}
void MinHeapFixDown(vector<int> &A,int i,int n){
	//找到左右孩子
	int left = 2*i+1;
	int right = 2*i+2;
	//考虑越界问题
	if(left>=n){
		return;
	}
	int min = left;
	if(right>=n){
		min=left;
	}else{//左右孩子都没越界
		if(A[right]<A[left]){
			min = right;
		}
	}
	//min指向了左右孩子中较小的那个
	
	//如果A[i]比两个孩子都要小,不用调整
    if(A[i]<=A[min]){
    	return;
    }
	//否则,找到两个孩子中较小的,和i交换
	swap(A[i],A[min]);
	
	//小孩子那个位置的值发生了变化,i变更为小孩子那个位置,递归调整
	MinHeapFixDown(A,min,n);
}
void MinHelp(vector<int> &A){
	int n=A.size();
	for(int i=n/2-1;i>=0;i--){
		MinHeapFixDown(A,i,n);
	}
}
int main()
{
	int arr[]={5,1,3,6,10,11,2,4,10,12};
	vector<int> vec(arr,arr+len(arr));
	MinHelp(vec);
	for(int i=0;i<vec.size();i++){
		cout<<vec[i]<<endl;
	}
	return 0;
}

堆排序(小顶堆)

#include<bits/stdc++.h>
using namespace std;
template <class T>
int len(T& arr){
	return sizeof(arr)/sizeof(arr[0]);
}
void MinHeapFixDown(vector<int> &A,int i,int n){
	//找到左右孩子
	int left = 2*i+1;
	int right = 2*i+2;
	//考虑越界问题
	if(left>=n){
		return;
	}
	int min = left;
	if(right>=n){
		min=left;
	}else{//左右孩子都没越界
		if(A[right]<A[left]){
			min = right;
		}
	}
	//min指向了左右孩子中较小的那个
	
	//如果A[i]比两个孩子都要小,不用调整
    if(A[i]<=A[min]){
    	return;
    }
	//否则,找到两个孩子中较小的,和i交换
	swap(A[i],A[min]);
	
	//小孩子那个位置的值发生了变化,i变更为小孩子那个位置,递归调整
	MinHeapFixDown(A,min,n);
}
void MinHeap(vector<int> &A,int n){
	for(int i=n/2-1;i>=0;i--){
		MinHeapFixDown(A,i,n);
	}
}
void sort(vector<int> &A,int n){
	//先对A进行堆化
	MinHeap(A,n);
	for(int i=0;i<A.size();i++){
		cout<<A[i]<<endl;
	}
	cout<<"===="<<endl;
	for(int i=n-1;i>0;i--)
	{ 
		//把堆顶,0号元素和最后一个元素对调
		swap(A[0],A[i]);
		//缩小堆的范围,对堆顶元素进行向下调整
		MinHeap(A,i);
	}		 
}
int main()
{
	int arr[]={5,1,3,6,10,11,2,4,10,12};
	vector<int> vec(arr,arr+len(arr));
	sort(vec,vec.size());
	for(int i=0;i<vec.size();i++){
		cout<<vec[i]<<endl;
	}
	return 0;
}

附:大顶堆

void MaxHeapFixDown(vector<int> &A,int i,int n){
	//找到左右孩子
	int left=2*i+1;
	int right=2*i+2;
	
	//看是否越界
	if(left>=n){
		return;
	}
	int max=left;
	if(right>=n){
		max=left;
	}else{
		if(A[right]>A[left])
			max=right;
	}
	
	//看根节点是否大于子节点
	if(A[i]>A[max]){
		return;
	}else{
		swap(A[i],A[max]);
	}
	MaxHeapFixDown(A,max,n);
}
void MaxHeap(vector<int> &A,int n){
	for(int i=n/2-1;i>=0;i--){
		MaxHeapFixDown(A,i,n);
	}
} 

计数排序

  • 一句话:用辅助数组对数组中出现的数字计数,元素转下标,小标转元素
  • 假设元素均大于等于0,一次扫描原数组,将元素值k记录在辅助数组的k位上
  • 一次扫描辅助数组,如果为1,将其插入目标数组的空白处

问题:

  • 有负数
  • 数据范围很大,比较稀疏,会导致辅助空间很大,造成空间浪费

数据范围较小或数据较密集适用
代码:

#include<bits/stdc++.h>
using namespace std;
template <class T>
int len(T& arr){
	return sizeof(arr)/sizeof(arr[0]);
}
void CountSort(vector<int> &A){
	int max = *max_element(A.begin(), A.end());
	vector<int> helper(max+1,0);
	for(int i=0;i<A.size();i++){
		helper[A[i]]++;
	}
	int current=0;
	for(int i=1;i<max+1;i++){
		while(helper[i]>0){
			A[current++]=i;
			helper[i]--;
		}
	}
}
int main()
{
	int arr[]={4,1,6,3,5,10,11,6,7};
	vector<int> vec(arr,arr+len(arr));
	CountSort(vec);
	for(int i=0;i<vec.size();i++){
		cout<<vec[i]<<endl;
	}
	return 0;
} 

桶排序

基数排序

发布了25 篇原创文章 · 获赞 0 · 访问量 1499

猜你喜欢

转载自blog.csdn.net/u014681799/article/details/86841768