排序(冒泡 插入 希尔 选择 归并 堆排序 快速排序)

一、冒泡:每次选择两个元素,按照需求进行交换(比如需要升序排列的话,把较大的元素放在靠后一些的位置),循环 n 次(n 为总元素个数),这样小的元素会不断 “冒泡” 到前面来,时间复杂度O(n^2)

void BubbleSort(int *arr,int len)
{
	int tmp = 0;
	bool flag = true;
	for(int i = 0;i<len && flag;i++)
	{
		flag = false;
		for(int j = 0;j<len-i-1;j++)
		{
			if(arr[j]>arr[j+1])
			{
				flag = true;
				tmp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = tmp;
			}
		}
	}
}

二、插入排序:每次选择一个元素,并且将这个元素和整个数组中的所有元素进行比较,然后插入到合适的位置,图片演示如上,时间复杂度 O(n^2)

代码如下:

void InsertSort(int *arr,int len)
{
	int tmp;
	for(int i = 1;i<len;i++)
	{
		tmp = arr[i];
		int j = i;
		for(j;j > 0;j--)
		{
			if(tmp < arr[j -1])
			{
				arr[j] = arr[j-1];
			}
			else
			{
				break;
			}
		}
		arr[j] = tmp;
	}
}

 三、希尔排序:对插入排序的改进。

static void InsertNum(int *arr, int len, int count)
{
	assert(NULL != arr);

	int tmp;
	for (int i = count; i < len; i += count)
	{
		tmp = arr[i];
		int j = i-count;
		for (; j >= 0; j -= count)
		{
			if (tmp < arr[j])
			{
				arr[j + count] = arr[j];
			}
			else
			{
				break;
			}
		}
		arr[j + count] = tmp;
	}
}


//时间复杂度O(n^1.3  -  n^1.5)  空间复杂度O(1)  不稳定
void ShellSort(int *arr, int len)
{
	assert(NULL != arr);
	int tmp[] = {5, 3, 1 };
	for (int i = 0; i < sizeof(tmp) / sizeof(tmp[0]);i++)
	{
		InsertNum(arr, len, tmp[i]);
	}
}

代码二:

int shellSort(int arr[], int n) 
{ 
    for (int gap = n/2; gap > 0; gap /= 2) 
    { 
        for (int i = gap; i < n; i += 1) 
        { 
            int temp = arr[i]; 
            int j;             
            for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) 
                arr[j] = arr[j - gap]; 
            arr[j] = temp; 
        } 
    } 
    return 0; 
} 

四、选择排序:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。时间复杂度O(n^2),稳定。 

代码如下:

void SelectSort(int *arr,int len)//bug
{
	int tmp = 0;
	for(int i = 0;i<len ;i++)
	{
		int sit = i;
		for(int j = i;j<len;j++)
		{
			if(arr[j]<arr[sit])
			{
				sit = j;
			}
		}
		tmp = arr[i];
		arr[i] = arr[sit];
		arr[sit]= tmp;
	}
}

五、 归并排序:加入了分治法的思想,时间复杂度O(nlogn) 空间复杂度O(n)。

其算法思路如下:

1.如果给的数组只有一个元素的话,直接返回(也就是递归到最底层的一个情况)

2.把整个数组分为尽可能相等的两个部分(分)

3.对于两个被分开的两个部分进行整个归并排序(治)

4.把两个被分开且排好序的数组拼接在一起

 

 

void MergeOnly(int *arr,int len,int group)
{
	assert(NULL != arr);
	int i = 0;
	int *tmp = (int *)malloc(len*sizeof(int));
	int low1 = 0;
	int high1 = low1 + group -1;
	int low2 = high1 + 1;
	int high2 = low2 + group -1 >=len -1 ? len-1:low2 + group -1;
	while(high1 <= len -1)//这里改成high1的越界条件更易理解
	{
		while(low1 <= high1 && low2 <= high2)
		{
			if(arr[low1] <= arr[low2])
			{
				tmp[i++] = arr[low1++];
			}
			else
			{
				tmp[i++] = arr[low2++];
			}
		}
		while(low1 <= high1)//没写的low1 写入
		{
			tmp[i++] = arr[low1++];
		}
		while(low2 <= high2)//没写的low2 写入tmp
		{
			tmp[i++] = arr[low2++];
		}
		low1 = high2+1;
		high1 = low1 + group -1;//high可能越界 但是high越界后low2一定越界就会跳出循环 而且后续不会用到high
		low2 = high1 + 1;
		high2 = low2 + group -1 >=len -1 ? len-1:low2 + group -1;
	}
	while(low1 <= len -1)
	{
		tmp[i++] = arr[low1++];
	}
	for(int j = 0;j < len;j++)
	{
		arr[j] = tmp[j];
	}
	free(tmp);
}
void MergeSort(int *arr,int len)//归并 时间复杂度O(logn)
{
	assert(NULL != arr);
	for(int i = 1;i<len;i*=2)
	{
		MergeOnly(arr,len,i);
	}
}

 六、快速排序:间复杂度并不固定,如果在最坏情况下(元素刚好是反向的)速度比较慢,达到 O(n^2)(和选择排序一个效率),但是如果在比较理想的情况下时间复杂度 O(nlogn)。

快排也是一个分治的算法,快排算法每次选择一个元素并且将整个数组以那个元素分为两部分,根据实现算法的不同,元素的选择一般有如下几种:

  • 永远选择第一个元素
  • 永远选择最后一个元素
  • 随机选择元素
  • 取中间值

整个快速排序的核心是分区(partition),分区的目的是传入一个数组和选定的一个元素,把所有小于那个元素的其他元素放在左边,大于的放在右边。

 

代码如下:

//快速排序
int Potation(int *arr,int low,int high)//找到low下标对应的数据在排序后的位置 
{
	assert(NULL != arr);
	int tmp = arr[low];
	while(low < high)
	{
		while(low < high && arr[high] >=  tmp)//先从后面找小于tmp的
		{
			high--;
		}
		if(low <high)
		{
			arr[low++] = arr[high];
		}
		while(low < high && arr[low] <= tmp)//从前面找大于tmp的
		{
		    low++;
		}
		if(low < high)
		{
			arr[high--] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}

void FastSort(int *arr,int low,int high)//一:递归做法
{
	assert(NULL != arr);
	if(low == high)
	{
		return ;
	}
	int sit = Potation(arr,low,high);
	if(low+1 < sit)
	{
		FastSort(arr,low,sit-1);
	}
	if(high-1 >sit)
	{
		FastSort(arr,sit+1,high);
	}
}
void FastStack(int *arr,int low,int high)//递归转入栈出栈的操作
{
	assert(NULL != arr);
	stack<int> sort;
	sort.push(low);
	sort.push(high);
	while(!sort.empty())
	{
		int high = sort.top();
		sort.pop();
		int low = sort.top();
		sort.pop();
		int sit = Potation(arr,low,high);
		if(low+1 < sit)
		{
			sort.push(low);
			sort.push(sit-1);
		}
		if(high-1 >sit)
		{
			sort.push(sit+1);
			sort.push(high);
		}
		
	}
	
}
void QuickSort(int *arr,int len)
{
	FastStack(arr,0,len-1);
}

七、堆排序:堆排序是一种基于二叉堆(Binary Heap)结构的排序算法,所谓二叉堆,我们通过完全二叉树来对比,只不过相比较完全二叉树而言,二叉堆的所有父节点的值都大于(或者小于)它的孩子节点,像这样:

首先需要引入最大堆的定义:

  • 最大堆中的最大元素值出现在根结点(堆顶)
  • 堆中每个父节点的元素值都大于等于其孩子结点

 

//堆排序
void sortonly(int *arr,int parent,int end)//做parent的运算 end是最后一个结尾的下标
{
	assert(NULL != arr);
	for(int i = parent*2+1;i<=end;i=2*i+1)
	{
		if(i+1 <= end && arr[i] < arr[i+1])
		{
			i++;
		}
		if(arr[i] > arr[parent])
		{
			int tmp = arr[i];
			arr[i] = arr[parent];
			arr[parent] = tmp;
			parent = i;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(int *arr,int len)//找到每一个parent 然后调用sortonly
{
	assert(NULL != arr);
	for(int i = (len-1-1)/2;i >= 0;i--)//i是每一个parent
	{
		sortonly(arr,i,len-1);//处理每一个parent结点
	}
	for(int i = 0;i<len-1;i++)//10个数的话 就只需要循环8次
	{
		int tmp = arr[0];
		arr[0] = arr[len-1-i];
		arr[len-1-i] = tmp;
		sortonly(arr,0,len-1-1-i);//第一次的end 最后的下标为len-1(最后一个元素)再减1,因为最后一个已经交换过,是最大的数字
	}
}

猜你喜欢

转载自blog.csdn.net/zDavid_2018/article/details/85418190
今日推荐