• leetcode刷题——排序和二分


    目录

    归并排序——分治

     练习题1 逆序对的数量

    练习题2 合并俩个有序数组

     快速排序——分治

     二分——整数二分

    浮点数二分 


    归并排序——分治

     

    共logn层,每层是O(n)

    时间复杂度O(nlogn)

    1.确定分界点,将数组按最中间的位置分开(下标的中间值)

    2.递归排序左边和右边

    3.左边和右边合二为一

    俩个有序数组,用指针移动的方法进行比较,min1指针此时所指的数

    这种情况当min1指针走到数组末尾时,把min2指针所指的数及其余的数全部放到数组P

     

    参考代码:

    1. void merge_sort(int* arr, int l, int r)
    2. {
    3. if (l >= r)
    4. return;
    5. int mid = (l + r) / 2;
    6. merge_sort(arr, l, mid );
    7. merge_sort(arr, mid+1, r);
    8. int tmp[100];
    9. int i = l;
    10. int j = mid + 1;
    11. int k = 0;
    12. while (i <= mid&&j<=r)
    13. {
    14. if (arr[i] < arr[j])
    15. tmp[k++] = arr[i++];
    16. else
    17. tmp[k++] = arr[j++];
    18. }
    19. while (i <= mid)
    20. tmp[k++] = arr[i++];
    21. while (j <= r)
    22. tmp[k++] = arr[j++];
    23. for (i = l,j=0; i <=r; i++,j++)
    24. {
    25. arr[i] = tmp[j];
    26. }
    27. }
    28. int main()
    29. {
    30. int n;
    31. scanf("%d", &n);
    32. int arr[100];
    33. int i = 0;
    34. for (i = 0; i < n; i++)
    35. {
    36. scanf("%d", &arr[i]);
    37. }
    38. merge_sort(arr, 0, n-1);
    39. for (i = 0; i < n; i++)
    40. {
    41. printf("%d ", arr[i]);
    42. }
    43. return 0;
    44. }

     练习题1 逆序对的数量

    给定一个长度为 nn 的整数数列,请你计算数列中的逆序对的数量。

    逆序对的定义如下:对于数列的第 ii 个和第 jj 个元素,如果满足 ia[j]a[i]>a[j],则其为一个逆序对;否则不是。

    输入:6
                2 3 4 5 6 1

    输出:5

    主要思想:

    先给俩边按从小到大顺序排序,当指针l所指元素大于指针j时,由于数组左右半边数组都是有序的说明l前面的元素都小于j所指元素,而l到mid,[l,mid]内的元素都大于j,因此逆序对数为:ret=mid-l+1。

    1. #include
    2. static long long ret = 0;
    3. long long merga_sore(int* arr, int l, int r)
    4. {
    5. if (l >= r)
    6. return 0;
    7. int mid = (l + r) / 2;
    8. merga_sore(arr, l, mid); merga_sore(arr, mid + 1, r);
    9. int i = l;
    10. int tmp[100000];
    11. int k = 0;
    12. int j = mid + 1;
    13. while (i <= mid && j <= r)
    14. {
    15. if (arr[i] <= arr[j])
    16. tmp[k++] = arr[i++];
    17. else
    18. {
    19. tmp[k++] = arr[j++];
    20. ret += (mid - i + 1);
    21. }
    22. }
    23. while (i <= mid)
    24. tmp[k++] = arr[i++];
    25. while (j <= r)
    26. tmp[k++] = arr[j++];
    27. for (i = l, j = 0; i <= r; i++, j++)
    28. {
    29. arr[i] = tmp[j];
    30. }
    31. return ret;
    32. }
    33. int main()
    34. {
    35. int arr[100000];
    36. int n;
    37. int i;
    38. scanf("%d", &n);
    39. for (i = 0; i < n; i++) scanf("%d", &arr[i]);
    40. long long c = merga_sore(arr, 0, n - 1);
    41. printf("%lld", c);
    42. return 0;
    43. }

     这里采用C代码解题

    练习题2 合并俩个有序数组

    点这里跳转

    1. void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    2. int i=m-1;
    3. int j=n-1;
    4. int k=m+n-1;
    5. while(i>=0&&j>=0)
    6. {
    7. if(nums1[i]>nums2[j])
    8. nums1[k--]=nums1[i--];
    9. else
    10. nums1[k--]=nums2[j--];
    11. }
    12. while(i>=0)
    13. nums1[k--]=nums1[i--];
    14. while(j>=0)
    15. nums1[k--]=nums2[j--];
    16. return (m+n-1);
    17. }

     比较end1和end2所指的元素,将较大元素放入k所指地址中,较小元素站在原地不动,等一个数组遍历结束后,另一个数组就是较小值,较小值放前面就行

     快速排序——分治

     

    1.确定分界点,q[left],q[right],q[(left+right)/2],随机值

    2.调整区间,小于等于X的在X左边,大于等于X的在X右边

    3.递归处理左右俩边,对左右俩边各进行排序

    如何进行第二步?

    方法1(暴力法):创建俩个数组a[]和b[],把<=x的元素放到a,>=x的元素放到b,然后把a数组放到q中,b也放到q中

    方法2(双指针):使用俩个指针,分别从左端点和右端点开始遍历数组,当左端点遇到>=x的元素时,左指针不再进行移动,此时左指针指向>=x的元素,然后等右端点遇到<=x的元素时,此时将这俩个元素进行交换,交换完之后俩指针继续往中间移动,同样的右端点若先遇到<=x的元素,则右端点停止遍历,在原地等左端点即可

    用3作为分界点,此时left=3则left指针停止前进,right满足>3,right向中间靠拢

    right此时不再满足>3,right停止前进,此时交换right和left所指向的俩个元素,交换完之后,各自向中间靠拢

    left所指元素满足<3,right所指元素不满足>3,right停止前进,left继续遍历

    left已经>right了,此时就不能交换俩个数,我们这个时候可以发现 <=3的在左边,>=3的在右边

    1. #include
    2. void quick_qsort(int* arr, int left, int right)
    3. {
    4. if (left >= right)
    5. return;
    6. int i = left-1;
    7. int j = right+1;
    8. int x = arr[left];
    9. while (i < j)
    10. {
    11. do
    12. {
    13. i++;
    14. } while (arr[i] < x);
    15. do
    16. {
    17. j--;
    18. } while (arr[j] > x);
    19. if (i < j)
    20. {
    21. int tmp = arr[i];
    22. arr[i] = arr[j];
    23. arr[j] = tmp;
    24. }
    25. }
    26. quick_qsort(arr, 0, j);
    27. quick_qsort(arr, j+1, right);
    28. }
    29. int main()
    30. {
    31. int arr[50];
    32. int n;
    33. scanf("%d", &n);
    34. int i = 0;
    35. for (i = 0; i < n; i++)
    36. {
    37. scanf("%d", &arr[i]);
    38. }
    39. quick_qsort(arr, 0, n - 1);
    40. for (i = 0; i < n; i++)
    41. {
    42. printf("%d ", arr[i]);
    43. }
    44. return 0;
    45. }

    注:当quick_qsort(arr, 0, j);函数里面为j时,x不能为arr[right],函数里面为i时,x不能为arr[left],这是边界问题,不然会陷入死循环

    当x=arr[left],qsort函数传参为i时,若给1,2排序,让x=1,i=left-1=-1,j=2-1+1=2; 之后i++,i=0,arr[0]=1,不满足x,然后j--,俩个都指向了1,j和i此时都是0,后面还有quick_qsort(arr, left, i-1);quick_qsort(arr, i, right);这俩个语句都是[0,1],会陷入死循环,当x=arr[right],qsort函数传参为j时,也会陷入死循环

    时间复杂度nlog(n),共logn层,每层是O(n)

     二分——整数二分

    整数二分:如果有单调性可以二分,部分没单调性的也可以二分

    本质:一个数组在绿色部分满足某种性质,在红色部分不满足,我们可以通过二分找到红色和绿色的边界点

    这里以红色为例

    1.找一个中间值mid=(l+r+1)/2

    2.判断这个中间值是否满足性质,看mid是否满足红色性质,若满足红色部分的性质,则mid在红色区域内,答案可能在[mid,r]中,因此让l=mid,

    当mid不满足红色性质,mid在绿色区域内,答案在[l,mid-1],之后让r=mid-1

    以绿色为例

    观察上式,l=mid,需要补1,r=mid不需要补1

    1. #include
    2. int main()
    3. {
    4.    int n;
    5.    int m;
    6.    scanf("%d%d", &n, &m);
    7.    int i = 0;
    8.    int arr[100000];
    9.    for (i = 0; i < n; i++)
    10.   {
    11.        scanf("%d", &arr[i]);
    12.   }
    13.    int k = 0;
    14.    while (m--)
    15.   {
    16.        scanf("%d", &k);
    17.        int l=0;
    18.        int r=n-1;
    19.        while(l
    20.       {
    21.            int mid=(l+r)/2;
    22.            if(arr[mid]>=k)
    23.            r=mid;
    24.            else
    25.            l=mid+1;
    26.       }
    27.        if(arr[l]!=k)
    28.        printf("-1 -1");
    29.        else
    30.       {
    31.            printf("%d ",l);
    32.            int l=0;
    33.            int r=n-1;
    34.            while(l
    35.           {
    36.                int mid=(l+r+1)/2;
    37.                if(arr[mid]<=k)
    38.                l=mid;
    39.                else
    40.                r=mid-1;
    41.           }
    42.            printf("%d ",l);
    43.       }
    44.        printf("\n");
    45.   }
    46.    return 0;
    47. }

    总结:从前面往后面判断k的位置,if(arr[mid]>=k),先判断k在不在前面,若不在前面,则往后找,找出来之后再对第二个k进行查找,if(arr[mid]<=k),此时从k在不在后面判断,若不在后面则往前找,这样查找会避免重复查找

    浮点数二分 

    浮点数二分不需要严格的处理边界,保证答案落在区间内

    当区间长度很小的时候,就可以当作答案,相当于估读

    1.先求一个浮点数的平方根

    2.每次找中间点(在0-x)

    3.如果mid*mid>=x,则说明,mid在根号x和x区间内,让r=mid,否则l=mid,之后打印就行

  • 相关阅读:
    1217. 玩筹码-数学推导法
    word文档怎么转换成pdf?几个实用文档转换方法
    深入理解JVM虚拟机第十篇:两个Class对象是否来源于一个类文件的标准以及类的主动使用与被动使用
    36. 有效的数独
    动态网页制作技术
    分布式事务最经典的八种解决方案
    服务器大请求体问题定位
    一个完整的数据分析案例 | 用Python搞定用户预测分析
    【响应式】使用媒体查询实现响应式页面
    螺杆支撑座对注塑机的生产过程有哪些重要影响?
  • 原文地址:https://blog.csdn.net/weixin_49449676/article/details/126021997