学习排序算法记录(java)

一、排序算法

1、冒泡排序

/*
 * 冒泡排序规则
 * (1) 一共进行 (数组的大小 - 1) 次大的循环
 * (2)每一趟排序的次数在逐渐的减少
 * (3) 如果我们发现在某趟排序中,没有发生一次交换, 可以提前结束冒泡排序。这个就是优化
 */
public void sort(int[] arr){
    for(int i = 0;i < arr.length-1;i++){
        boolean flag = false;
        for(int j = 0;j < arr.length - i - 1;j++){
            if(arr[j] > arr[j+1]){
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                flag = true;
            }
        }
        if(!flag){
            return;
        }
    }
}

2、插入排序

public void sort(int[] arr){
    for(int i = 1;i < arr.length;i++){
        int insertVal = arr[i]; //待插入的值
        int insertIndex = i-1; //插入索引
        //寻找插入位置,从有序表的最后一个位置从后向前遍历比较,如果插入值小于遍历的值,则遍历的值赋给遍历值的后一位
        if(insertVal < arr[insertIndex]){
            while(insertIndex >= 0 && insertVal < arr[insertIndex]){
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            //跳出while循环表示已经找到插入位置,将插入值赋给索引为(插入索引+1)的位置
            arr[insertIndex+1] = insertVal;
        }
    }
}

3、选择排序

/*
 * 说明:
 * 1. 选择排序一共有 (数组大小 - 1) 轮排序
 * 2. 每1轮排序,又是一个循环, 循环的规则:
 * 2.1 先假定当前这个数是最小数
 * 2.2 然后和后面的每个数进行比较,如果发现有比当前数更小的数,就重新确定最小数,并得到下标
 * 2.3 当遍历到数组的最后时,就得到本轮最小数和下标
 * 2.4 交换
 */
public void sort(int[] arr){
    for(int i = 0;i < arr.length-1;i++){
        int min = arr[i]; //假定arr[i]为最小数
        int index_min = i; //记录最小数的索引
        //从第i+1个数开始寻找最小数,如果找到了,则将找到的数记录为最小数以及记录最小数的索引
        for(int j = i+1;j < arr.length;j++){
            if(arr[j] < min){
                min = arr[j];
                index_min = j;
            }
        }
        //判断如果最小数的索引不等于i,表示i后面的数有比假定的最小数更小的,则进行交换
        if(index_min!=i){
            arr[index_min] = arr[i];
            arr[i] = min;
        }
    }
}

4、希尔排序

/*
希尔排序是插入排序的优化,可以参考插入排序
希尔排序是将数组分类gap组,gap从arr.length/2开始,到1结束
对gap组的每一组进行插入排序
 */
public void sort(int[] arr){
    //将数据分成gap组
    for(int gap = arr.length/2;gap > 0;gap /=2){
        //遍历各组中所有的元素
        for(int i = gap;i < arr.length;i++){
            int insertVal = arr[i];
            int insertIndex = i - gap;
            //将该元素插入到它所在组中的正确的位置(使用插入排序)
            if(insertVal < arr[insertIndex]){
                while(insertIndex >= 0 && insertVal < arr[insertIndex]){
                    arr[insertIndex + gap] = arr[insertIndex];
                    insertIndex-=gap;
                }
                arr[insertIndex + gap] = insertVal;
            }

        }
    }
}

5、归并排序

public void sort(int[] arr,int left,int right,int[] temp){
    if(left < right) {
        int mid = (left + right) / 2; //中间索引
        //向左递归进行分解
        sort(arr, left, mid, temp);
        //向右递归进行分解
        sort(arr, mid + 1, right, temp);
        //合并
        merge(arr, left, mid, right, temp);
    }
}
//合并的方法
/**
 *
 * @param arr 排序的原始数组
 * @param left 左边有序序列的初始索引
 * @param mid 中间索引
 * @param right 右边索引
 * @param temp 做中转的数组
 */
public void merge(int[] arr,int left,int mid,int right,int[] temp){
    int i = left; // 初始化i, 左边有序序列的初始索引
    int j = mid + 1; //初始化j, 右边有序序列的初始索引
    int t = 0; // 指向temp数组的当前索引

    //1
    //先把左右两边(有序)的数据按照规则填充到temp数组
    //直到左右两边的有序序列,有一边处理完毕为止
    while(i <= mid && j <= right){
        if(arr[i] <= arr[j]){
            temp[t] = arr[i];
            i++;
            t++;
        }else{
            temp[t] = arr[j];
            j++;
            t++;
        }
    }

    //2
    //把有剩余数据的一边的数据依次全部填充到temp
    while(i <= mid){
        temp[t] = arr[i];
        i++;
        t++;
    }
    while(j <= right){
        temp[t] = arr[j];
        j++;
        t++;
    }

    //3
    //将temp数组的元素拷贝到arr
    //注意,并不是每次都拷贝所有
    t = 0;
    int tempLeft = left;
    while(tempLeft <= right){
        arr[tempLeft] = temp[t];
        t++;
        tempLeft++;
    }
}

6、快速排序

public void sort(int[] arr,int left, int right) {
    //如果right <= left则表示以及排序完毕
    if(right <= left){
       return;
    }

    //选择数组中索引值为left的值作为基准值
    //最终会将数组调整为基准值左边都是小于等于基准值的数,右边都是大于等于基准值的数
    int chooseVal = arr[left];
    //创建两个遍历指针
    int l =left;
    int r =right;
    while(l < r){
        //从数组遍历右边的指针位置向前遍历
        //如果遇到小于基准值的数就将该值放在遍历左边的指针位置,然后遍历左边的指针向后移动一位
        while(l < r && arr[r] >= chooseVal){
           r--;
        }
        if(l < r){
            arr[l] = arr[r];
            l++;
        }
        //从数组遍历左边的指针位置向后遍历
        //如果遇到大于基准值的数就将该值放在遍历右边的指针位置,然后遍历右边的指针向前移动一位
        while(l < r && arr[l] <= chooseVal){
            l++;
        }
        if(l < r){
            arr[r] = arr[l];
            r--;
        }
    }
    //跳出循环后,表示左右指针进行了重合,将基准值放在该索引位置
    arr[l] = chooseVal;

    //将基准值的左边作为新的数组进行快排
    sort(arr, left, l-1);
    //将基准值的右边作为新的数组进行快排
    sort(arr, l+1, right);
}

7、堆排序

public void sort(int[] arr){
    //构造一个临时变量存储中间数据
    int temp = 0;//1、先将无序序列构建成一个堆
    for(int i = arr.length/2-1;i >= 0;i--){
        adjustHeap1(arr,i,arr.length);
    }
    //2、将堆顶元素与末尾元素交换,将最大(最小)元素换到数组末端
    //3、重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
    for(int i = arr.length - 1;i > 0;i--){
        temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
        adjustHeap1(arr,0,i);
    }
}

/**
 * 将一个数组二叉树(可以是完整的二叉树,也可以是子二叉树)调整为大顶堆
 * @param arr 待调整的完整的数组
 * @param i 在数组中索引值为i的数,调整的是以该数为根节点的二叉树,该数所在的二叉树的结点为非叶子结点
 * @param length 需要调整的数组的长度
 */
public void adjustHeap1(int[] arr,int i,int length){
    //记录在数组中索引值为i的数
    int temp = arr[i];

    //利用for循环开始调整
    //k = 2*i+1是i结点的左子结点
    for(int k = 2*i+1;k < length;k = k*2+1){
        //判断i结点的左结点大还是右结点大,取较大的结点
        if(k+1 < length && arr[k] < arr[k+1]){
            k++;
        }
        //判断子结点的值是否大于父结点的值,如果大于,则将子结点的值赋给父结点,否则就调整完毕
        if(arr[k] > temp){
            arr[i] = arr[k];
            //由于我们的调整是从树的最下面开始向上调整,害怕交换完子父结点的值之后,破坏了以该子结点为父结点的子二叉树
            //的结构,因此将k赋给i继续检查子二叉树的结构
            i = k;
        }else{
            //由于我们的调整是从树的最下面开始向上调整,因此这里如果子结点的值都小于父结点,表示该二叉树已经调整为大顶堆
            break;
        }
    }
    //将temp值赋给arr[i],注意,这里可能已经将k的值赋给了i,因此是i表示子结点的位置了
    arr[i] = temp;
}

/**
 * 将一个数组二叉树(可以是完整的二叉树,也可以是子二叉树)调整为小顶堆
 * @param arr 待调整的完整的数组
 * @param i 在数组中索引值为i的数,调整的是以该数为根节点的二叉树,该数所在的二叉树的结点为非叶子结点
 * @param length 需要调整的数组的长度
 */
public void adjustHeap2(int[] arr,int i,int length){
    //记录在数组中索引值为i的数
    int temp = arr[i];

    //利用for循环开始调整
    //k = 2*i+1是i结点的左子结点
    for(int k = 2*i+1;k < length;k = k*2+1){
        //判断i结点的左结点小还是右结点小,取较小的结点
        if(k+1 < length && arr[k] > arr[k+1]){
            k++;
        }
        //判断子结点的值是否小于父结点的值,如果小于,则将子结点的值赋给父结点,否则就调整完毕
        if(arr[k] < temp){
            arr[i] = arr[k];
            //由于我们的调整是从树的最下面开始向上调整,害怕交换完子父结点的值之后,破坏了以该子结点为父结点的子二叉树
            //的结构,因此将k赋给i继续检查子二叉树的结构
            i = k;
        }else{
            //由于我们的调整是从树的最下面开始向上调整,因此这里如果子结点的值都小于父结点,表示该二叉树已经调整为大顶堆
            break;
        }
    }
    //将temp值赋给arr[i],注意,这里可能已经将k的值赋给了i,因此是i表示子结点的位置了
    arr[i] = temp;
}

二、测试算法

public static void main(String[] args) {
    int[] arr = {4,5,8,9,5,10,11,15,13,-2,-99,101};
    sort(arr);
    System.out.println(Arrays.toString(arr));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值