基本思想:
将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。即:首先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录开始与子序列逐个进行比较,找到插入位置插入,原来位置上的元素向后顺移逐个进行插入,直至整个序列有序为止。
如果子序列中遇到与插入元素相等的,置于该元素后面
要点:设立哨兵,作为临时存储和判断数组边界之用
时间复杂度:O(n^2)
使用场景:小规模数据或基本有序时
private static void rapidSort(int[] arr) {
int length = arr.length;
for (int i = 1; i < length; i++) {
int x=arr[i]; //将插入元素设为哨兵
int j=i-1;
while(j>=0&&x<arr[j]){
//哨兵依次与子序列元素比较
arr[j+1]=arr[j]; //子序列元素往后移一位
j--;
}
arr[j+1]=x;
printAll(arr,i); //打印当前数组
}
}
基本思想:
算法过程:
时间复杂度:平均O(n1.3),最坏O(n2)
private static void shellSort(int[] arr) {
int length=arr.length;
for (int gap=length/2;gap>0;gap/=2){
//进行分组,第一步的增量gap为数组长度一半,逐步减半
for (int i=gap;i<length;i++){
//对各个分组进行插入排序
insert(arr,gap,i); //将arr[i]插入到组内正确位置
}
}
}
private static void insert(int[] arr, int gap, int i) {
int inserted=arr[i];
int j;
for (j=i-gap;j>=0&&inserted<arr[j];j-=gap){
//组内元素两两相隔gap
arr[j+gap]=arr[j];
}
arr[j+gap]=inserted;
}
基本思想:
时间复杂度:O(n^2)
private static void selectSort(int[] arr) {
int length = arr.length;
for (int i = 0; i < length; i++) {
int minKey=getMinKey(arr,length,i); //获取其它剩余元素中最小元素的索引
if(i!=minKey){
//与其交换
int temp=arr[i];
arr[i]=arr[minKey];
arr[minKey]=temp;
}
printAll(arr,i+1);
}
}
private static int getMinKey(int[] arr, int length, int i) {
int k=i;
for (int j=i;j<length;j++){
//遍历寻找最小索引
if(arr[j]<arr[k]){
k=j;
}
}
return k;
}
基本思想:
建基本堆
时间复杂度:Ο**(nlogn)
/**
* 堆排序算法
*/
void HeapSort(int H[],int length)
{
//初始堆
BuildingHeap(H, length);
//从最后一个元素开始对序列进行调整
for (int i = length - 1; i > 0; --i)
{
//交换堆顶元素H[0]和堆中最后一个元素
int temp = H[i];
H[i] = H[0];
H[0] = temp;
//每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整
HeapAdjust(H,0,i);
}
}
/**
* 初始堆进行调整
* 将H[0..length-1]建成堆
* 调整完之后第一个元素是序列的最小的元素
*/
void BuildingHeap(int H[], int length)
{
//最后一个有孩子的节点的位置 i= (length -1) / 2
for (int i = (length -1) / 2 ; i >= 0; --i)
HeapAdjust(H,i,length);
}
/**
*@Param heap是待调整的数组
*@Param s是待调整数组元素的位置
*@Param length是待调整数组的长度
**/
void HeapAdjust(int H[],int s, int length)
{
int tmp = H[s];
int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置)
while (child < length) {
if(child+1 <length && H[child]<H[child+1]) {
// 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点)
++child ;
}
if(H[s]<H[child]) {
// 如果较大的子结点大于父结点
H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点
s = child; // 重新设置s ,即待调整的下一个结点的位置
child = 2*s+1;
} else {
// 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出
break;
}
H[s] = tmp; // 当前待调整的结点放到比其大的孩子结点位置上
}
print(H,length);
}
基本思想:
时间复杂度:O(n^2)
void BubbleSort(int arr[], int length)
{
for (int i = 0; i < length; i++)
{
for (int j = 0; j < length - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int temp;
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
冒泡排序改进:
1.加入一标志性变量exchange,用于标志某一趟排序过程中是否有数据交换,如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程。
//设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。
//由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。
public static void newBubbleSort_01(int arr[],int length){
int i=length-1;
while(i>0){
int pos=0; //设置标志变量初始记载零号元素
for(int j=0;jarr[j+1]){
int tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
pos=j; //发生交换时,记录位置
}
}
i=pos; //为下一趟循环设置上限
}
}
2.传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半
public static void newBubbleSort(int[] arr,int length){
int low=0;
int high=length-1; //设置变量最小值
while(low<high){
for(int i=0;i<high;i++){
//正向冒泡找到最大值
if(arr[i]>arr[i+1]){
int tmp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=tmp;
}
}
high--; //修改上限值,前移一位
for(int i=high;i>low;i--){
//反向冒泡找到最小值
if(arr[i]<arr[i-1]){
int tmp=arr[i];
arr[i]=arr[i-1];
arr[i-1]=tmp;
}
}
low++; //修改下限值,后移一位
}
}
基本思想:
1、先从数列中取出一个数作为基准数
2、分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
3、再对左右区间重复第二步,直到各区间只有一个数
快速排序是通常被认为在同数量级(O(nlog2n))的排序方法中平均性能最好的,但初始序列按关键码有序或基本有序时,快排序反而蜕化为冒泡排序
当待排序的关键字是随机分布时,快速排序的平均时间最短;
时间复杂度:O(NlogN)
一次排序过程
private static void quickSort(int[] arr,int l,int r) {
if (l<r) {
int length = arr.length;
int i=l,j=r;
int x=arr[i]; //选择第一个数为基准数
while(i<j){
while(i<j&&arr[j]>x){
//从右至左寻找小于x的数
j--;
}
if(i<j)
arr[i++]=arr[j];
while(i<j&&arr[i]<x){
//从左至右寻找大于x的数
i++;
}
if(i<j)
arr[j--]=arr[i];
}
arr[i]=x;
quickSort(arr,l,i-1); //递归调用调整左右两个队列
quickSort(arr,i+1,r);
}
}
基本思想:
时间复杂度:O(NlogN)
空间复杂度:O(N)
稳定性:归并排序是稳定的排序算法,temp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
这行代码可以保证当左右两部分的值相等的时候,先复制左边的值,这样可以保证值相等的时候两个元素的相对位置不变。
相关:
private static void mergeSort(int[] arr) {
sort(arr,0,arr.length-1); //对原始数组进行切分
}
private static void sort(int[] arr, int l, int r) {
if(l==r) //左边际等于右边际时表示不能再切分,此时数组元素数量为一
return;
int mid=(l+r)/2; //取中值
sort(arr,l,mid); //切分左边数组
sort(arr,mid+1,r); //切分右边数组
merge(arr,l,mid,r); //将两边已排序数组合成一个合并数组
}
private static void merge(int[] arr, int l, int mid, int r) {
int[] temp=new int[r-l+1]; //创立一个辅助数组暂存排完序的合并数组
int ele1=l; //左边数组的开头元素
int ele2=mid+1; //右边数组的开头元素
int i=0;
while(ele1<=mid&&ele2<=r){
temp[i++]=arr[ele1]<=arr[ele2]?arr[ele1++]:arr[ele2++]; //将两边数组里的元素逐一比较后放入辅助数组
}
while(ele1<=mid){
//两边数组里其余未放入辅助数组的数接到末尾
temp[i++]=arr[ele1++];
}
while(ele2<=r){
temp[i++]=arr[ele2++];
}
for (int j = 0; j < temp.length; j++) {
arr[l+j]=temp[j]; //将辅助数组的数全部复制到原数组
}
}
基本思想:
分类:
LSD——从低位向高位排(从个位开始排)
MSD——从高位向低位排
时间复杂度:O(d(r+n)),最差时O(d(r+n))
排序过程:
//LSD
//arr是要排序的数组,max是数组中最大的数有几位
public static void radixSort(int[] arr,int max)
{
//count数组用来计数
int[] count = new int[10];
//bucket用来当桶(在下面你就理解了什么是桶了),放数据,取数据
int[] bucket = new int[arr.length];
//k表示第几位,1代表个位,2代表十位,3代表百位
for(int k=1;k<=max;k++)
{
//把count置空,防止上次循环的数据影响
for(int i=0;i<10;i++)
{
count[i] = 0;
}
//分别统计第k位是0,1,2,3,4,5,6,7,8,9的数量
//以下便称为桶
//即此循环用来统计每个桶中的数据的数量
for(int i=0;i<arr.length;i++)
{
count[getFigure(arr[i],k)]++;
}
//利用count[i]来确定放置数据的位置
for(int i=1;i<10;i++)
{
count[i] = count[i] + count[i-1];
}
//执行完此循环之后的count[i]就是第i个桶右边界的位置
//利用循环把数据装入各个桶中,注意是从后往前装
//这里是重点,一定要仔细理解
for(int i=arr.length-1;i>=0;i--)
{
int j = getFigure(arr[i],k);
bucket[count[j]-1] = arr[i];
count[j]--;
}
//将桶中的数据取出来,赋值给arr
for(int i=0;i<arr.length;i++)
{
arr[i] = bucket[i];
}
printAll(arr,k);
}
}
//此函数返回整型数i的第k位是什么
public static int getFigure(int i,int k)
{
int[] a = {
1,10,100};
return (i/a[k-1])%10;
}
参考自