常用的查找与排序算法

                                                                           常用的查找与排序算法

 

目录

工具 

1.生成随机数组

2.测试排序算法的运行时间

3.打印输出

排序

1.冒泡排序

原理

实现

复杂度

2.选择排序

原理

实现

复杂度

3.插入排序

原理

实现

时间复杂度

4.归并排序

原理

实现

结果

查找:

1.顺序查找

原理

实现

 2.折半查找

原理:

实现:

3.二叉查找树

原理

实现


  • 工具 

1.生成随机数组

  • rand() 的内部实现是用线性同余法做的,它不是真的随机数,因其周期特别长,故在一定的范围里可看成是随机的。
  • rand() 返回一随机数值的范围在 0RAND_MAX 间。RAND_MAX 的范围最少是在 32767 之间(int)。用 unsigned int 双字节是 65535,四字节是 4294967295 的整数范围。0~RAND_MAX 每个数字被选中的机率是相同的。
  • rand() 产生的是伪随机数字,系统默认的随机数种子为 1。每次执行时是相同的; 若要不同, 用函数 srand() 初始化它。
void generateArr(int arr[],int n,int rangL,int rangR)
{
    //随机数种子
    srand(time(NULL));
    for(int i=0;i<n;i++)
        arr[i]=rand()%(rangR-rangL-1);
}
int main()
{
    std::cout<<"please input num: "<<std::endl;
    int n;
    cin>>n;
    int arr[n];
    generateArr(arr,n,5,100);
    for(int i=0;i<n;i++)
    {
        std::cout<<arr[i]<<" ";
    }
    std::cout<<std::endl;
    return 0;

}

2.测试排序算法的运行时间

void test_sort(string sort_name,void(*sort)(int [],int),int arr[],int n)
{
    clock_t star_time=clock();
    sort(arr,n);
    clock_t end_time=clock();
    std::cout<<sort_name<<" time: "<<double(end_time-star_time)/CLOCKS_PER_SEC<<std::endl;
}

3.打印输出

void PrintArray(int *arr,int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}
  • 排序

1.冒泡排序

  • 原理

每一轮,每两个元素比较大小并进行交换,直到这一轮当中最大(最小)的元素被放置在数组的尾部,然后不断地重复这个过程,直到所有元素都排好位置。其中,核心操作就是元素相互比较。

  • 实现

void BubbleSort1(int* num,int n)
{
    int temp;
    for(int i=0;i<n-1;i++)
    {
        for(int j=0;j<n-1-i;j++)
        {
            if(num[j]<num[j+1])
            {
                temp=num[j];
                num[j]=num[j+1];
                num[j+1]=temp;
            }
        }
    }
}
  • 优化版本: 判断前一轮中是否进行过交换,如果没有,则表示已经有序了;否则,为无序的。
void BubbleSort2(int* num,int n)
{
    int temp;
    int flag=1;//无序标志位
    for(int i=0;i<n-1&&flag==1;i++)
    {
        flag=0;
        for(int j=0;j<n-1-i;j++)
        {
            if(num[j]>num[j+1])
            {
                temp=num[j];
                num[j]=num[j+1];
                num[j+1]=temp;
                flag=1;
            }
        }
    }
}
  • 复杂度

  • 最好的情况:数组已排序好

需要进行n−1次的比较,两两交换次数为O(1),时间复杂度是O(n)。

  • 最坏的情况:数组逆序排列

需要进行 n(n-1)/2 次比较,时间复杂度是 O(n^2)。

  • 一般情况:

平均时间复杂度是 O(n^2)。

2.选择排序

  • 原理

第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。

  • 实现

void select_sort(int arr[],int n)
{
	int mindex;
	for(int i=0;i<n;i++)
	{
		mindex=i;
		for(int j=i+1;j<n;j++)
		{
			if(arr[mindex]>arr[j])
			{
				mindex=j;
			}
		}
		swap(arr[mindex],arr[i]);	
	}
}
  • 复杂度

  • 选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。
  • 唯一的好处可能就是不占用额外的内存空间了吧。

3.插入排序

  • 原理

  • 实现

template <typename T>
void sort_insert(T arr[],int n)
{
    for(int i=1;i<n;i++)
    {
        for(int j=i;j>0;j--)
            if(arr[j]>arr[j-1])
                swap(arr[j],arr[j-1]);
            else
                break;
    }
}
  • 优化版本:
template <typename T>
void sort_insert_1(T arr[],int n)
{
    for(int i=1;i<n;i++)
    {
        T e=arr[i];
        int j;//终止的位置
        for(j=i;j>0;j--)
        {
            if(arr[j-1]>e)
                arr[j]=arr[j-1];
            else
                break;
        }
        arr[j]=e;
    }
}
  • 时间复杂度

4.归并排序

  • 原理

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

归并排序分为三个过程:

  1. 将数列划分为两部分(在均匀划分时时间复杂度为  );
  2. 递归地分别对两个子序列进行归并排序;
  3. 合并两个子序列。

 注:图1来源于五分钟学算法

  • 实现

  • 自顶向下
void _merge(int arr[],int l,int mid,int r)
{
	//分配大小为r-l+1的辅助数组 
	int aux[r-l+1];
	for(int i=l;i<=r;i++)
	{
		//偏移量为l 
		aux[i-l]=arr[i];
	}
	int i=l,j=mid+1;
	for(int k=l;k<=r;k++)
	{
		//左边用尽,取右边的元素 
		if(i>mid)
		{
			arr[k]=aux[j-l];
			j++;
		}
		//右边用尽,取左边的元素 
		else if(j>r)
		{
			arr[k]=aux[i-l];
			i++;
		}
		//右边元素大于左边,取左边 
		else if(aux[i-l]<aux[j-l])
		{
			arr[k]=aux[i-l];
			i++;
		}
		//左边元素大于等于右边,取右边 
		else
		{
			arr[k]=aux[j-l];
			j++;
		}			
	}
	
}
void _merge_sort(int arr[],int l,int r)
{
	if(l>=r)
		return;
	int mid=l+(r-l)/2;
	//归并排序[l,mid] 
	_merge_sort(arr,l,mid);
	//归并排序[mid+1,r] 
	_merge_sort(arr,mid+1,r);
	//归并 
	_merge(arr,l,mid,r);		
}
void merge_sort(int arr[],int n)
{
	//范围[l,r] 
	_merge_sort(arr,0,n-1);
} 
int main()
{
	int n=10;
	int *arr=new int[n];
	generateRandomArray(arr,n,10,50);
	PrintArray(arr,n);
	merge_sort(arr,n);	
	PrintArray(arr,n);
//	test_sort("select_sort",select_sort,arr,n);
} 

优化:

1._merge之前判断是否有序

 

2.对小规模子数组使用插入排序

void insert_sort_3(int arr[],int l,int r)
{
	for(int i=l;i<=r;i++)
	{
		int e=arr[i];
		int j;
		for(j=i;j>l;j--)
		{
			if(arr[j-1]>e)
				arr[j]=arr[j-1];
			else
				break;				
		}
		swap(arr[j],e);
	}
}
  • 自顶向上

归并:从子序列长度为1(k)开始,进行两两归并,得到2*k的有序序列;

循环:子序列长度为2k开始,进行两两归并,终止条件直到原数组遍历完成。

void merge_sortBU(int arr[],int n)
{
	//k:当前子序列的长度 
	for(int k=1;k<n;k+=k)
		for(int low=0;low+k<n;low+=2*k)
			_merge(arr,low,low+k-1,min(low+k+k-1,n-1));
	
}
int main()
{
	int n=10;
	int *arr=new int[n];
	generateRandomArray(arr,n,0,20);
	PrintArray(arr,n);
	merge_sortBU(arr,n);	
	PrintArray(arr,n);
//	test_sort("select_sort",select_sort,arr,n);
} 
  • 结果

 

查找:

1.顺序查找

  • 原理

  • 思想: 

顺序查找就是在关键字集合中找出与给定的关键字相等的元素。

  • 步骤:

(1)从文件的第一个记录开始,将每个记录的关键字与给定的关键字比较
(2)如果查找到某个记录的关键字等于 key,则查找成功,返回该记录在文件中的位置;如果所有的记录都进行了比较,仍未找到与 k 相等的记录,则给出 0,表示查找失败。

  • 时间复杂度:O(n)
  • 实现

#include <iostream>
using namespace std;
typedef struct student
{
    unsigned  int id;
    char name[10];
    int score;
}Student;
int searchinfo(Student *stu,int num,int id)
{
    for(int i=0;i<num;i++)
    {
        if(stu[i].id==id)
            return i;
    }
    return -1;
}
int main()
{
    Student stu[4]={{1004,"TOM",100},
                    {1002," LILY",95},
                    {1001,"ANN",93},
                    {1003,"luCY",98}};
    int address;
    address=searchinfo(stu,4,1001);
    std::cout<<"ID: "<<stu[address].id<<std::endl;
    std::cout<<"name: "<<stu[address].name<<std::endl;
    std::cout<<"score: "<<stu[address].score<<std::endl;
    return  0;
}

 

 2.折半查找

  • 原理:

  • 思想: 

二分查找的思想是利用分治法,逐渐将查找的数据集范围缩小。

  • 具体过程:

将待查的关键字 k 与当前查找范围内位置居中的关键字比较,如果相等,则查找成功,返回被查到记录在文件中的位置;如果 k 小于当前居中记录的关键字,则对当前查找范围的前半部分重复上述过程,否则到当前查找范围的后半部分重复上述过程。如果查找失败返回NULL。

Image result for 二分查找 gif

  • 时间复杂度:O(log2n)

  • 实现:

  • 闭区间:[l,r]  
#include<iostream>
using namespace std;

int Binary_search(int arr[],int n,int target)
{
	int l=0,r=n-1;//在[l,r]闭区间搜索target 
	while(l<=r)//当l==r时,区间依然有效 
	{
		int mid=l+(r-l)/2;
		if(arr[mid]==target)
		{
			return mid+1;	
		}
		else if(arr[mid]<target)
		{
			l=mid+1;//在[mid+1,r]闭区间搜索target	
		}
		else
		{
			r=mid-1;//在[l,mid-1]闭区间搜索target
		} 	
	}
	return -1;
 }
 int main()
 {
 	int arr[5]={0,1,5,6,7};
 	int index=Binary_search(arr,5,10);
 	cout<<index<<endl;
 	return 0;
  } 
  • 半开区间:[l,r)
#include<iostream>
using namespace std;

int Binary_search(int arr[],int n,int target)
{
	int l=0,r=n;//在[l,r)区间搜索target 
	while(l<r)//当l==r时,区间无效 
	{
		int mid=l+(r-l)/2;
		if(arr[mid]==target)
		{
			return mid+1;	
		}
		else if(arr[mid]<target)
		{
			l=mid+1;//在[mid+1,r)区间搜索target	
		}
		else
		{
			r=mid;//在[l,mid-1)区间搜索target
		} 	
	}
	return -1;
 }
 int main()
 {
 	int arr[5]={0,1,5,6,7};
 	int index=Binary_search(arr,5,7);
 	cout<<index<<endl;
 	return 0;
  } 
  • 注意:

 二分查找的区间问题。

3.二叉查找树

  • 原理

  • 简介:

二叉查找树是一种具有良好排序查找性能的二叉树数据结构。二叉查找树支持多种基本操作,包括查找、按序遍历、求最大值和最小值、查找前驱结点和后继结点、插入和删除结点等。一般将它用于查找字典或优先队列结构。这些操作在二叉查找树上的平均执行时间是 O( log2 n )。

  • 定义:

1. 它是一棵二叉树,如果根结点的左子树不空,则左子树中所有的结点值均小于根结点。
2. 如果根结点的右子树不为空,则右子树中所有的结点值均大于根结点。
3. 二叉查找树的每一棵子树也是一棵二叉查找树。

  • 实现

#include<iostream>
using namespace std;
typedef struct node{
	int data;
	struct node *left;
	struct node *right;
}Node;

void create_tree(Node *Tree, int data)
{
	//初始化新结点 
	Node* new_node=new Node;
	new_node->data=data;
	new_node->left=NULL;
	new_node->right=NULL; 
	
	Node* cur=Tree;	
	while(cur)
	{
		
		if(cur->data<data)
		{	
			//右节点为空,直接将新节点连接在右节点上 
			if(cur->right==NULL)
			{
	
				cur->right=new_node;
				return ;
			}
			//右节点不为空,更新当前节点为右节点 
			else
			{
				cur=cur->right;

			}
		}
		//同理 
		else
		{
			if(cur->left==NULL)
			{
				cur->left=new_node;
				return ;
			}
			else
			{
				cur=cur->left;
			}
		}
	}
}
void middle_search(Node* Tree)
{
	if(!Tree)
		return;
	middle_search(Tree->left);
	cout<<Tree->data<<" ";
	middle_search(Tree->right);
}
int main()
{
	Node* Tree=new Node;
	Tree->left=NULL;
	Tree->right=NULL;
	Tree->data=10;
	create_tree(Tree,12);
	create_tree(Tree,9);
	create_tree(Tree,50);
	create_tree(Tree,20);
	middle_search(Tree);
	return 0;
 } 
发布了44 篇原创文章 · 获赞 26 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/He3he3he/article/details/102596160