排序
实现归并排序、快速排序、插入排序、冒泡排序、选择排序、堆排序(选做)
1.冒泡
/**
* 冒泡排序
* 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
* 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
* 针对所有的元素重复以上的步骤,除了最后一个。
* 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
*/
public static void bubbleSort(int[] a){
int temp=0;
for(int i=0;i<a.length-1;i++){
for(int j=0;j<a.length-1-i;j++){
if(a[j]>a[j+1]){
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
}
2.选择
/**
* 选择排序算法
* 在未排序序列中找到最小元素,存放到排序序列的起始位置
* 再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。
* 以此类推,直到所有元素均排序完毕。
*/
public static void selectSort(int[] a) {
int temp=0;
for(int i=0;i<a.length;i++){
int k=i;//待确定的位置
for(int j=a.length-1;j>i;j--){
if(a[j]<a[k]){//选择出应该在第i个位置的数
k=j;
}
}//交换两个数
temp=a[i];
a[i]=a[k];
a[k]=temp;
}
}
3.插入
/**
* 插入排序
*
* 从第一个元素开始,该元素可以认为已经被排序
* 取出下一个元素,在已经排序的元素序列中从后向前扫描
* 如果该元素(已排序)大于新元素,将该元素移到下一位置
* 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
* 将新元素插入到该位置中
* 重复步骤2
*/
public static void insertSort(int[] a) {
int temp = 0 ;
int j = 0;
for(int i = 0 ; i <a.length ; i++)
{
temp = a[i];
//假如temp比前面的值小,则将前面的值后移
for(j = i ; j > 0 && temp < a[j-1] ; j --)
{
a[j] = a[j-1];
}
a[j] = temp;
}
}
4.希尔
基本思想:
希尔排序的实质就是分组插入排序,又称缩小增量法。
将整个无序序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序时,再对全体元素进行一次直接插入排序。
因为直接插入排序在元素基本有序的情况下,效率是很高的,因此希尔排序在时间效率上有很大提高。
实例:
无序序列:int a[] = {3,1,5,7,2,4,9,6};
第一趟时: n=8; gap=n/2=4; 把整个序列共分成了4个子序列{3,2}、{1,4}、{5,9}、{7,6}
第二趟时:gap=gap/2=2; 把整个序列共分成了2个子序列{2,5,3,9}、{1,6,4,7}
第三趟时:对整个序列进行直接插入排序
/**希尔排序的原理:根据需求,如果你想要结果从大到小排列,它会首先将数组进行分组,然后将较大值移到前面,较小值
* 移到后面,最后将整个数组进行插入排序,这样比起一开始就用插入排序减少了数据交换和移动的次数,可以说希尔排序是加强
* 版的插入排序
*/
public static void shellSort(int[] a) {
int j = 0;
int temp = 0;
for (int gap = a.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < a.length; i++) {
temp = a[i];
for (j = i; j >= gap; j-=gap) {
if(temp<a[j-gap]){
a[j]=a[j-gap];
}else{
break;
}
}
a[j]=temp;
}
}
}
5.归并
/**
* 归并排序
* 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
* 时间复杂度为O(nlogn)
* 稳定排序方式
*/
public static void mergeSort(int[] a,int low,int high){
int mid = (low + high) / 2;
if (low < high) {
mergeSort(a, low, mid);
mergeSort(a, mid + 1, high);
merge(a, low, mid, high);
}
}
public static void merge(int[] a,int low,int mid, int high){
int[] temp = new int[high - low + 1];
int i = low;// 左指针
int j = mid + 1;// 右指针
int k = 0;
// 把较小的数先移到新数组中
while (i <= mid && j <= high) {
if (a[i] < a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
}
// 把左边剩余的数移入数组
while (i <= mid) {
temp[k++] = a[i++];
}
// 把右边边剩余的数移入数组
while (j <= high) {
temp[k++] = a[j++];
}
// 把新数组中的数覆盖a数组
for (int k2 = 0; k2 < temp.length; k2++) {
a[k2 + low] = temp[k2];
}
}
6.快排
public static void quickSort(int[] a,int low,int high){
if(low<high) {
int pivotpos = partition(a, low, high);
quickSort(a, low, pivotpos - 1);
quickSort(a, pivotpos + 1, high);
}
}
public static int partition(int[] a,int low,int high) {
int pivot = a[low];
while (low < high) {
while (low < high && a[high] > pivot) {
--high;
}
a[low]=a[high];
while (low < high && a[low] <= pivot) {
++low;
}
a[high]=a[low];
}
a[low]=pivot;
return low;
}
7.堆排
1、基本思想:
堆排序是一种树形选择排序,是对直接选择排序的有效改进。
堆的定义下:具有n个元素的序列 (h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二 叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。
思想:初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
2、实例
初始序列:46,79,56,38,40,84
建堆:
交换,从堆中踢出最大数
/**
*
* 堆排序
*
*/
public class Sort {
public static void main (String[] args){
int[] a={3,1,5,7,2,4,9,6};
//int[] a={3,1,5,7,2,4,9,6,10,4,6};
//bubbleSort(a);
//selectSort(a);
//insertSort(a);
//shellSort(a);
//quickSort(a,0,a.length-1);
//mergeSort(a,0,a.length-1);
for(int i=0;i<a.length-1;i++){
//建堆
buildMaxHeap(a,a.length-1-i);
//交换堆顶和最后一个元素
swap(a,0,a.length-1-i);
}
System.out.println(Arrays.toString(a));
}
public static void buildMaxHeap(int[] data, int lastIndex){
//从lastIndex处节点(最后一个节点)的父节点开始
for(int i=(lastIndex-1)/2;i>=0;i--){
//k保存正在判断的节点
int k=i;
//如果当前k节点的子节点存在
while(k*2+1<=lastIndex){
//k节点的左子节点的索引
int biggerIndex=2*k+1;
//如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
if(biggerIndex<lastIndex){
//若果右子节点的值较大
if(data[biggerIndex]<data[biggerIndex+1]){
//biggerIndex总是记录较大子节点的索引
biggerIndex++;
}
}
//如果k节点的值小于其较大的子节点的值
if(data[k]<data[biggerIndex]){
//交换他们
swap(data,k,biggerIndex);
//将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
k=biggerIndex;
}else{
break;
}
}
}
}
//交换
private static void swap(int[] data, int i, int j) {
int tmp=data[i];
data[i]=data[j];
data[j]=tmp;
}
}
完成leetcode上的返回滑动窗口中的最大值(239)
LeetCode 239 Sliding Window Maximum
本人链接:https://blog.csdn.net/susuxuezhang/article/details/89282543
编程实现 O(n) 时间复杂度内找到一组数据的第 K 大元素
LeetCode 215 Kth Largest Element in an Array
本人链接:https://blog.csdn.net/susuxuezhang/article/details/89284808
二分查找
1.实现一个有序数组的二分查找算法
public static int binarySearch(int[]arr,int target) {
int l = 0, r = arr.length - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (arr[mid] > target) {
r = mid - 1;
} else if (arr[mid] < target) {
l = mid + 1;
} else {
return mid;
}
}
return -1;
}
2.实现模糊二分查找算法(比如大于等于给定值的第一个元素)
public static int binarySearch_2(int[]arr,int target) {
int l = 0, r = arr.length - 2;
if(target<=arr[0]){
return arr[0];
}
while (l <= r) {
int mid = (l + r) / 2;
if (arr[mid] < target && arr[mid+1] >= target) {
return arr[mid+1];
} else if(arr[mid+1] < target){
l=mid+1;
}else if(arr[mid] >= target){
r = mid;
}
}
return -1;
}