データ構造とアルゴリズムの美しさ(ソート)

いくつかの基本的なソートとその時間計算量

ここに画像の説明を挿入
最も古典的で一般的に使用されるソート方法のいくつか:バブルソート、挿入ソート、選択ソート、クイックソート、マージソート、カウントソート、基数ソート、バケットソート。

第二に、ソートアルゴリズムを分析する方法は?

並べ替えアルゴリズムのパフォーマンスを分析する方法は?ソートアルゴリズムのパフォーマンスは、実行効率、メモリ消費、安定性の3つの側面から分析されます。

1.実行効率(以下の3つの側面から測定)

1)最良の場合、最悪の場合、平均的な場合の時間計算量
2)時間計算量の係数、一定、低次:ソートされたデータの量は考慮に入れるのが比較的少ない
3)比較の数と交換(または移動)の数

2.メモリ消費

アルゴリズムのメモリ消費量は、スペースの複雑さによって測定でき、ソートアルゴリズムも例外ではありません。ただし、並べ替えアルゴリズムのスペースの複雑さのために、新しい概念である「並べ替え」も導入しました。インプレースソートアルゴリズムは、スペースの複雑さがO(1)のソートアルゴリズムです。今日お話ししている3つの並べ替えアルゴリズムは、すべてインプレース並べ替えアルゴリズムです。

3.安定性

実行効率とメモリ消費量だけでソートアルゴリズムの品質を測定するだけでは不十分です。並べ替えアルゴリズムには、重要な指標である安定性もあります。この概念は、ソートされるシーケンスに等しい値を持つ要素がある場合、ソート後、等しい要素の元の順序は変更されないままであることを意味します。

例を挙げて説明します。たとえば、データセット2、9、3、4、8、3があります。サイズで並べ替えると、2、3、3、4、8、9になります。

このデータセットには2つの3があります。特定のソートアルゴリズムでソートした後、2つの3の順序が変わらない場合、このソートアルゴリズムを安定ソートアルゴリズムと呼びます 。2つの順序が変更された場合、対応するソートアルゴリズムは不安定ソートアルゴリズムと呼ばれます

第三に、ソートアルゴリズム

1.バブルソート

1)実行効率:最小時間計算量、最大時間計算量、平均時間計算量

仕分けの原則

1)バブルソートは、2つの隣接するデータに対してのみ機能します。
2)2つの隣接するデータを比較して、サイズの関係の要件を満たしているかどうかを確認し、満たしていない場合は、2つを交換します。
3)1回のバブリングにより、少なくとも1つの要素が本来あるべき位置に移動し、n回繰り返され、n個のデータの並べ替えが完了します。
4)最適化:特定のバブリングでデータ交換が行われない場合は、完全な順序が達成されたことを意味するため、バブリングは終了します。

パフォーマンス分析

実行効率:最小時間計算量、最大時間計算量、平均時間計算量
最小時間計算量:データが完全に順序付けられている場合、必要なバブリング操作は1つだけで、時間計算量はO(n)です。
最大時間計算量:データを逆の順序で並べ替える場合、n回のバブリング操作が必要であり、時間計算量はO(n ^ 2)です。
平均時間計算量:順序と反転によって分析されます。

秩序とは何ですか?
順序度は、配列内で順序付けられた関係を持つ要素のペアの数です。たとえば
、[2、4、3、1、5、6]このデータグループの順序度は11であり、[ 2、4] [2、3] [2,5] [2,6] [4,5] [4,6] [3,5] [3,6] [1,5] [1,6] [ 5,6]。同様に、[6,5,4,3,2,1]などの逆配列の場合、順序の次数は0です。[1,2,3,4,5,6]などの完全に順序付けられた配列の場合]、次数はn *(n-1)/ 2、つまり15であり、完全な次数は完全次数と呼ばれます。

反転度とは何ですか?反転の程度の定義は、順序の程度の正反対です。コアフォーミュラ:リバースオーダー=フルオーダー-オーダー。
並べ替えプロセスは、順序の度合いを上げ、反転の度合いを減らし、最終的に完全な順序に到達するプロセスであり、並べ替えが完了したことを示します。

2)スペースの複雑さ

交換ごとに必要な一時変数は1つだけなので、スペースの複雑さはO(1)であり、これはインプレースソートアルゴリズムです。

3)アルゴリズムの安定性

2つの値が等しい場合、位置は交換されないため、安定したソートアルゴリズムです。

2.挿入ソート

アルゴリズムの原理

まず、配列内のデータを、並べ替えられた間隔と並べ替えられていない間隔の2つの間隔に分割します。最初にソートされた範囲には、配列の最初の要素である要素が1つだけあります。挿入アルゴリズムの中心的な考え方は、並べ替えられていない間隔の要素を取得し、並べ替えられた間隔で適切な挿入位置を見つけて挿入し、並べ替えられた間隔の要素が常に正しいことを確認することです。ソートされていない要素が空になり、アルゴリズムが終了するまで、このプロセスを繰り返します。

パフォーマンス分析

1)時間計算量:最良、最悪、平均的なケース

並べ替える配列がすでに並べ替えられている場合は、データを移動する必要はありません。配列をトラバースする必要があるのは1回だけなので、時間計算量はO(n)です。

配列が逆の順序である場合、各挿入は配列の最初の位置に新しいデータを挿入することと同等であるため、大量のデータを移動する必要があるため、時間計算量はO(n ^ 2)になります。

配列に要素を挿入する平均時間計算量はO(n)であり、挿入ソートにはn回の挿入が必要であるため、平均時間計算量はO(n ^ 2)です。

2)スペースの複雑さ

上記のコードからわかるように、挿入ソートアルゴリズムの操作には追加のストレージスペースは必要ないため、スペースの複雑さはO(1)であり、これはインプレースソートアルゴリズムです。

3)アルゴリズムの安定性

挿入ソートでは、同じ値の要素について、前に表示された要素の後に後に表示される要素を挿入することを選択できます。これにより、元の順序が変更されず、安定します。

3.マージソート

追加予定...

提案されたトピック(ライセンス)

912.配列の並べ替え(補足質問4.手でクイックソートを引き裂く)

https://leetcode-cn.com/problems/sort-an-array/

整数の配列numsを指定して、配列を昇順で並べ替えてください。

例1:

入力:
nums = [5,2,3,1]
出力:
[1,2,3,5]
例2:
入力:
nums = [5,1,1,2,0,0]
出力:
[0,0 、1,1,2,5]

ヒント:

1 <= nums.length <= 5 * 104
-5 * 104 <= nums [i] <= 5 * 104

/*
只能说不讲武德  这个题目要优化快排,一开始有点莽  直接冲快排 然后不出意外的超时了  不死心的还去交了一下改进的代发   一样卡在第11 12哥测试答案上,然后就看了一下题解  题解说到了基准值改为随机函数取的一个值,然后去看了快排优化的几种思路;
关于快排 O(logn)  圈重点——————》不稳定  
第一种
    就是最原始的  取一个固定值当基准值 然后去对比 一般就是取第一个或者最后一个了  但是如果输入序列是随机的,处理时间可以接受的。如果数组已经有序时,此时的分割就是一个非常不好的分割。因为每次划分只能使待排序序列减一,此时为最坏情况,快速排序沦为冒泡排序了,时间复杂度为Θ(n^2)。而且,输入的数据是有序或部分有序的情况是相当常见的。
第二种
    就是这个这个题  用随机函数去取基准值  这样就不会造成刚刚所说的那个情况  除非整个数组的数据一模一样的 时间复杂度又会回到Θ(n^2);
    其实我感觉也没有优化什么  就只是避免了第一种所说的情况而已;
       (ps:亲测 我把随机函数改了  直接让中间的值来当基准值)
第三种
    三数取中法  这个可以弥补第二种的缺陷 但是它也比哪俩种都难写 这个不讲(因为我也太了解 hhhh 网上还有什么三路快排让我先去琢磨一下子)
*/
class Solution {
    
    
    int quicksort(vector<int>& nums, int l, int r) {
    
    
        int temp = nums[r];
        int i = l - 1;
        for (int j = l; j <= r - 1; ++j) {
    
    
            if (nums[j] <= temp) {
    
    
                i = i + 1;
                swap(nums[i], nums[j]);
            }
        }
        swap(nums[i + 1], nums[r]);
        return i + 1;
    }
    int partition(vector<int>& nums, int l, int r) {
    
    
        int i = (r+l)>>1; // 取中间的值当基准值
        swap(nums[r], nums[i]);
        return quicksort(nums, l, r);
    }
    void Sort(vector<int>& nums, int l, int r) {
    
    
        if (l < r) {
    
    
            int pos = partition(nums, l, r);
            Sort(nums, l, pos - 1);
            Sort(nums, pos + 1, r);
        }
    }
public:
    vector<int> sortArray(vector<int>& nums) {
    
    
        srand((unsigned)time(NULL));
       Sort(nums, 0, (int)nums.size() - 1);
        return nums;
    }
};
//快排必须要用随机函数  不然就会超时

82.ソート済みリストIIの重複要素を削除する

ソートされたリンクリストの先頭を指定して、元のリンクリスト内の重複する番号を持つすべてのノードを削除し、個別の番号のみを残します。ソートされたリンクリストを返します。

例1:
ここに画像の説明を挿入

入力:ヘッド= [1,2,3,3,4,4,5]
出力:[1,2,5]

例2:
ここに画像の説明を挿入

入力:ヘッド= [1,1,1,2,3]
出力:[2,3]

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* deleteDuplicates(ListNode* head) {
    
    
        if (head==NULL||head->next==NULL){
    
      //先判断是否为空
            return head;
        }
        if (head->val!=head->next->val){
    
     //如果当前值和下一个不一样,那么这个数据就是合法的,不需要删除,那么就将下一个递归,检查是否需要删除
            head->next=deleteDuplicates(head->next);
            return head;
        }
        
        else {
    
    
            int value=head->val;  //记录值
            while (head!=NULL&&head->val==value){
    
       //删除重复的所以值
                head=head->next;
            }
            if (head==NULL) // 如果是空,那么直接返回
                return NULL;
                head=deleteDuplicates(head);   //因为这个head就是下一个待检查的数据,所以不需要加next,一开始我加了,然后数据少了,有时候有多了  所以这个还是要注意一下,否则很容易出错
                return head;
        
        } 
    }
};

おすすめ

転載: blog.csdn.net/qq_54729417/article/details/123345047