記事ディレクトリ
一種の
ソート処理中に、ソート対象のすべてのレコードがメモリに配置されているかどうかによって、ソートは内部ソートと外部ソートに分けられます。
内部ソート:ソート対象のすべてのレコードがメモリに配置されます。
外部ソート:ソート レコードの数が多すぎてメモリに配置できないため、ソート プロセスでは、内部メモリと外部メモリの間でデータを複数回交換する必要があります。
ソート処理中にソート対象のレコードを比較するかどうかによって、比較ソートと非比較ソートに分けることができます。
比較ソート:バブルソート、選択ソート、挿入ソート、マージソート、ヒープソート、クイックソートなど
非比較ソート:基数ソート、バケットソート、カウントソートなど
ソートアルゴリズムに影響を与える 3 つの指標
私たちが通常参照するソート アルゴリズムは、内部ソート アルゴリズムです. 内部ソートの場合、主に 3 つの側面の影響を受けます。それらは、時間性能、補助空間 (空間性)、およびアルゴリズムの複雑さです。
1.指標1 - タイムパフォーマンス
内部ソートには、比較と移動という 2 つの主な操作があります。比較はキーワード間の比較であり、移動はある場所から別の場所へのレコードの移動を指します。したがって、並べ替えアルゴリズムの時間オーバーヘッドは、その品質の重要な指標です。
2. インジケータ 2 - 補助スペース
補助スペースとは、アルゴリズムの品質を測定するもう 1 つの基準である、並べ替えによって占有されるストレージ スペースに加えて、アルゴリズムを実行するために必要なその他のストレージ スペースを指します。
3. 指標 3 - アルゴリズムの複雑さ
ここでのアルゴリズムの複雑さは、時間の複雑さではありません。アルゴリズムの複雑さに応じて、単純なアルゴリズムと改良されたアルゴリズムの 2 つのカテゴリに分けることができます。
シンプルなアルゴリズム: バブル、選択、挿入
改善されたアルゴリズム: ヒル、ヒープ、マージ、クイック ソート
構造とその他の機能
構造定義
typedef int ElemType;
#define MAXSIZE 10
#define N 10
typedef struct {
ElemType data[MAXSIZE];//存储的数据元素
int length;// 线性表当前长度
}SqList;//线性表
交換機能
/*交换线性表L中下标为i和j的元素*/
void Swap(SqList &L,int i,int j) {
int temp=L.data[i];
L.data[i]=L.data[j];
L.data[j]=temp;
}
線形テーブルを初期化する
/* 初始化顺序线性表 */
bool InitList(SqList &L)
{
L.length=MAXSIZE;
return true;
}
線形リストの要素を出力する
/*输出线性表中的元素*/
void PrintList(const SqList &L){
for(int i=0;i<L.length;i++)
cout<<L.data[i]<<" ";
cout<<endl;
}
1. バブルソート
交換ソートの一種であるバブルソート (Bubble Sort) は、基本的な考え方は、隣接するレコードのキーワードを 2 つずつ比較し、逆順のレコードがなくなるまで逆順になっている場合は交換します。
元データが格納されている配列に対して、前から後ろに複数回スキャンを行い、各スキャンを1パスとし、2つのデータのサイズがビット列と一致しない場合、データを入れ替える。ソートされた場合、最大の数字を最後に置きます。各比較は各パスの最大数を底に沈めるため、各パスの比較数は徐々に減少します。
特徴: 昇順ソートでは、比較の各ラウンドで最大数が下に沈むため、各ラウンドでの相互比較の数は、前のラウンドよりも 1 つ少なくなります。
N 個の数値を N-1 回 (丸め) 並べ替え、昇順の並べ替え (大きなシンク) と降順の並べ替え (小さなシンク) を比較します。
バブルソート ベーシックエディション
/*
冒泡排序初级版
不算是标准的冒泡排序算法,因为不满足
"两两比较相邻记录的" 的冒泡排序思想。
算法思路:
让每一个数据元素和后一个数据元素
进行比较,如果大则进行交换,在第一趟
比较,最后一个元素一定是最大值。
*/
void EasyBubbleSort(SqList &L){
for(int i=0;i<L.length-1;i++)//n个数比较 n-1趟
for(int j=0;j<L.length-i;j++)//比较n-i次
if(L.data[j]>L.data[j+1])
Swap(L,j,j+1) ;//交换 L->data[i]与 L->data[j]的值
}
通常のバブルソート
/*冒泡排序*/
void BubbleSort(SqList &L) {
for(int i=0;i<L.length-1;i++)
for(int j=L.length-1;j>=i;j--)//将L.data[i] 元素归位
if(L.data[j]>L.data[j+1])//相邻的练个元素反序时
Swap(L,j,j+1);//将 L->data[i]与 L->data[j]的值 交换
}
改善されたバブル ソート
/*
冒泡排序改进
假如序列{2,1,3,4}
如果比较第一二次之后,在之前
的冒泡排序还会一直比下去,从i=1一直
执行到i=3,数量越大,则时间耗得越长,
为了避免这种情况,设计了一个标志位。
在每一次交换过后,置为1,循环判断如果为1则执行
否则跳过本趟比较。
*/
void BubbleSortImprove(SqList &L){
bool flag= true;
for(int i=0;i<L.length-1&&flag;i++){
flag=false;
for(int j=L.length-1;j>=i;j--){
if(L.data[j-1]>L.data[j]){
Swap(L,j-1,j);
flag=true;//如果有数据交换,则flag为true
}
}
}
}
バブルソートの総時間計算量は O(n^2) です
2.簡易選択ソート
アルゴリズムの考え方: n-i+1 個のレコードの中から、n 回のキーワードを比較して最小のキーワードを持つレコードを選択し、i 番目 (1<=i<=n) のレコードと交換します。
この最小のキーワードを持つ要素を選択する単純な比較方法が、単純選択ソートの名前の由来です。
/*简单选择排序*/
void SelectSort(SqList &L){
for(int i=0;i<L.length;i++){
int min=i;//将当前的下标定义为最小值下标
for(int j=i+1;j<L.length;j++) {
if(L.data[min]>L.data[j]){
//如果当前的值比下标为min中的还小
min=j;//把这个下标给min
}
}
if(i!=min) //如果min与当前i值不想等,说明找到最小值
Swap(L,i,min);
}
}
単純選択ソートとバブル ソートの時間計算量は O(n^2) ですが、単純選択ソートのパフォーマンスはバブル ソートよりわずかに優れています。
3. 挿入ソート
直接挿入ソート (配列の最初の要素をバッファーとして使用)
アルゴリズムのアイデア: 並べ替えられた順序付きリストにレコードを挿入すると、レコード数が 1 つ増えた新しい順序付きリストが得られます。
/*
直接插入排序:
第一种写法, 数组的第一个元素不存储,起到一个缓冲器的作用
线性表中的第一个元素在这里起到哨兵的作用
当前元素如果比后一个元素小,那么将当前元素设置为哨兵,
再把当前元素之前的所有元素与哨兵进行比较,如果比哨兵小,
那么哨兵为这个元素,反之,则进行交换。
所以,最后一次比较,哨兵是最后一轮的最小元素。
*/
void InsertSort(SqList &L) {
int i,j;
for( i=2;i<L.length;i++){
if(L.data[i]<L.data[i-1]){
L.data[0]=L.data[i];//设置哨兵,哨兵为每轮比较的最小元素
for( j=i-1;L.data[j]>L.data[0];j--){
L.data[j+1]=L.data[j];//记录后移 这里的j+1其实是i
}
L.data[j+1]=L.data[0];//交换元素,把哨兵插入到正确的位置
}
}
}
この書き込み方法のソート レコードがランダムである場合、比較と移動の平均数は ≈ (n 2)/4 であり、時間計算量は O(n 2) ですが、直接挿入ソートのパフォーマンスはそれよりわずかに優れています。バブリングと選択ソートの。
アルゴリズムのアイデア: 順序付けされていない領域の開始要素 a[i] (1<=i<=n-1) を順序付けられた領域 a[0...i-1] の適切な位置に挿入すると、a[0 ...i ] が新しい順序付き領域になります。
直接挿入ソート(直接交換)
/*
直接插入排序
第二种写法,不采用数组第一个元素为缓冲器,
默认第一个元素是有序的,直接进行交换
*/
void InsertSort2(SqList &L) {
for(int i=1;i<L.length;i++){
int key=L.data[i];
int j=i-1;
while(j>=0&&L.data[j]>key){
//定位,当前一个元素比key大
L.data[j+1]=L.data[j];//插入,大记录后移,小记录前移
j--;
}
//如果j<0,则说明依次与前面进行比较比完了,此时是data[0]=key;
//如果data[j]<key,则不比较data[j],key的值不变。
L.data[j+1]=key;
}
}
説明: 2 番目の書き込み方法の各パスで生成される順序付け領域は、グローバルな順序付け領域であるとは限りません、つまり、順序付け領域内の要素が最終位置に配置されるとは限りません。 .
バイナリ挿入ソート
アルゴリズムの考え方: 非順序領域の先頭要素 a[i] (1<=i<=n-1) を順序領域 a[0...i-1] に挿入し、逐次比較の方法を使用します。ここでは二分探索の手法を用いて a[0...i-1] 内の挿入位置を見つけて要素を移動することを二分挿入ソートとも呼びます。
/*
折半插入排序
利用二分查找定位,再进行插入排序
*/
void BinInsertSort(SqList &L){
int i,j,low,mid,high;
for( i=1;i<L.length;i++){
if(L.data[i]<L.data[i-1]){
int tmp=L.data[i];
low=0;
high=i-1;
while(low<=high){
//在L.data[low...high] 中查找插入的位置
mid=(low+high) /2;//取中间位置
if(tmp<L.data[mid])
high=mid-1;//插入点在左半区间
else
low=mid+1;//插入点在右半区间
}
for(j=i-1;j>=high+1;j--) {
L.data[j+1]=L.data[j];
}
L.data[high+1]=tmp;
}
}
}
直接挿入ソートと比較すると、ハーフソートは移動要素のパフォーマンスを向上させず、キーワードの比較回数を減らすだけです. 平均的なパフォーマンスの観点からは、ハーフ検索はシーケンシャル検索よりも優れているため、ハーフ挿入ソートも直接よりも優れています.挿入ソート. 挿入ソートの空間複雑度は O(1) です.
4.クイックソート
バブルソートで改良されており、ソートするn個の要素の中からランダムに要素(通常は最初の要素)をベンチマークとして選び、その要素を適切な位置に配置した後、これでデータ列を分割するというのが基本的な考え方です。要素 2 つの部分。要素より小さい要素を手前に、要素より大きい要素を奥に、要素を2つの要素の真ん中に配置する(要素ホーミングとなる)この処理をクイックソート(パス部門ともいう)。
次に、生成された 2 つのパーツに対して上記のプロセスを繰り返し、各パーツに要素が 1 つだけになるか、空になるまで繰り返します。簡単に言えば、各分割は、テーブルの最初の要素を適切な位置に配置し、テーブルを2つに分割し、分割されたサブテーブルの長さが1または0になるまで、サブテーブルでこの分割を再帰的に続けます。
/*
快排
*/
void QuickSort(SqList &L,int l,int r){
//判断合法性 递归结束
if(l>=r) return ;
int key=L.data[l],i=l-1,j=r+1;
while(i<j){
//保证左边的<key,右边的>key
do i++;while(L.data[i]<key) ;
do j--;while(L.data[j]>key);
if(i<j)Swap(L,i,j);
}
QuickSort(L,l,j);
QuickSort(L,j+1,r);
}
5.マージソート
基本的な考え方:
マージのアイデアを使用して実装されたソート方法は、初期シーケンスに n レコードが含まれていると仮定すると、n 個の順序付けられたサブシーケンスと見なすことができ、各サブシーケンスの長さは 1 であり、ペアでマージすると、⌈n を取得できます。 / 2⌉ (⌈x⌉ は x 以上の最小の整数を表す) は、長さ 2 または 1 の順序付けられた部分シーケンスを 2 つずつマージし、長さ n の順序付けられたシーケンスが得られるまでこれを繰り返します。
/*
归并排序
*/
int temp[MAXSIZE];
void MergeSort(SqList &L, int l,int r){
if(l>=r)return ;
//划分区间
int mid=(l+r)/2;
MergeSort(L,l,mid);
MergeSort(L,mid+1,r);
int k=0,i=l,j=mid+1;
//把两边区间最小的数存入temp中
while(i<=mid&&j<=r) {
if(L.data[i]<=L.data[j])
temp[k++]=L.data[i++];
else
temp[k++]=L.data[j++];
}
//把两边任一剩下的数存入temp中
while(i<=mid)
temp[k++]=L.data[i++];
while(j<=r)
temp[k++]=L.data[j++];
//从左边开始把temp中的数放回至a中
for(i=l,j=0;i<=r;i++,j++)
L.data[i]=temp[j];
}
7 つのアルゴリズムのパフォーマンスのまとめ
ソート方法 | 時間の複雑さ | スペースの複雑さ | 安定 | 複雑 | ||
---|---|---|---|---|---|---|
平均的なケース | 最悪の場合 | 最良の場合 | ||||
バブルソート | O(n^2) | O(n^2) | の上) | O(1) | 安定させる | 単純 |
単純選択ソート | O(n^2) | O(n^2) | O(n^2) | O(1) | 不安定 | 単純 |
直接挿入ソート | O(n^2) | O(n^2) | の上) | O(1) | 安定させる | 単純 |
バイナリ挿入ソート | O(n^2) | O(n^2) | の上) | O(1) | 安定させる | 単純 |
クイックソート | O(nlogn) | O(n^2) | O(nlogn) | O(ログ) | 不安定 | より複雑 |
双方向マージソート | O(nlogn) | O(nlogn) | O(nlogn) | の上) | 安定させる | より複雑 |