バブルソートのアルゴリズム分析と改良
交換ソートの基本的な考え方は、ソート対象のレコードのキーワードをペアごとに比較し、2つのレコードの順序が逆であることが判明した場合、交換ソートを行うというものです。逆順でレコードがなくなるまで実行されます。
エクスチェンジソートの基本的な考え方を適用した主なソート方法は、バブルソートとクイックソートです。
public class BubbleSort implements SortUtil.Sort{ public void sort(int[] data) { int temp; for(int i=0;i<data.length;i++){ for(int j=data.length-1;j>i;j--){ if(data[j]<data[j-1]){ SortUtil.swap(data,j,j-1); } } } }
バブルソート
1. ソート方法
ソートされたレコード配列 R[1..n] を縦に並べ、各レコード R を R.key の重みを持つバブルとみなします。軽い泡は重い泡の下には存在できないという原則に従って、配列 R は下から上に走査されます。この原則に違反する軽い泡は上に「浮く」ことになります。最後の 2 つの泡が上部の軽い泡と下部の重い泡になるまで、これが繰り返されます。
(1) 最初の
R[1..n]は、順序付けされていない領域です。
(2) 最初のスキャン
は、無秩序な領域の下部から上に向かって 2 つの隣接するバブルの重みを比較し、軽い方が下部にあり、より重い方が上部にあることが判明した場合、その位置を決定します。二人は交換される。つまり、(R[n], R[n-1])、(R[n-1]、R[n-2])、...、(R[2]、R[1]) を比較します。シーケンス; 各ペア Bubble (R[j+1], R[j]) について、R[j+1].key
(3) 2 番目のスキャン
R[2..n] をスキャンします。スキャンが完了すると、「2 番目の光」のバブルが R[2] の位置に浮かび上がります...
最後に、n-1 回のスキャンの後、順序付けされた領域 R[1..n] を取得できます
注: i 番目のスキャン、R[1..i-1] と R[i..n] はそれぞれ現在の順序付けされた領域と順序付けされていない領域です。スキャンは依然として、乱れた領域の下部から領域の上部まで行われます。スキャンが完了すると、エリア内の最も明るいバブルが最上位の位置 R に浮上し、その結果、R[1..i] が新しい順序付けされたエリアになります。
2. バブルソート処理の例
キーワードシーケンス 49 38 65 97 76 13 27 49
3. ソートアルゴリズム
(1) 分析
各パスのソートが 1 つ追加されるためn-1 個のソートの後、順序付けされた領域には n-1 個のバブルが存在し、無秩序領域内のバブルの重みは常に順序付けられた領域内のバブルの重み以上になります。したがって、バブル ソート プロセス全体で必要なソート操作は最大でも n-1 回です。
特定の選別パスでバブルの位置の交換が見つからない場合、選別対象の無秩序領域内のすべてのバブルが上部の軽いバブルと下部の重いバブルの原則を満たしていることを意味します。ソートプロセスはこのパスでソートでき、その後終了します。このため、以下に示すアルゴリズムではブール交換が導入され、各並べ替えが開始される前に FALSE に設定されます。並べ替え中に交換が発生した場合は TRUE に設定します。交換は各ソート パスの最後にチェックされます。交換が発生しない場合、アルゴリズムは終了し、次のソート パスは実行されません。
(2) 具体的なアルゴリズム
void BubbleSort(SeqList R) { //R(l..n)是待排序的文件,采用自下向上扫描,对R做冒泡排序 int i,j; Boolean exchange; //交换标志 for(i=1;i<n;i++){ //最多做n-1趟排序 exchange=FALSE; //本趟排序开始前,交换标志应为假 for(j=n-1;j>=i;j--) //对当前无序区R[i..n]自下向上扫描 if(R[j+1].key<R[j].key){//交换记录 R[0]=R[j+1]; //R[0]不是哨兵,仅做暂存单元 R[j+1]=R[j]; R[j]=R[0]; exchange=TRUE; //发生了交换,故将交换标志置为真 } if(!exchange) //本趟排序未发生交换,提前终止算法 return; } //endfor(外循环) } //BubbleSort
4.
(1)算法的最好时间复杂度
若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:
Cmin=n-1
Mmin=0。
冒泡排序最好的时间复杂度为O(n)。
(2)算法的最坏时间复杂度
若初始文件是反序的,需要进行n-1趟排序。每趟排序要进行n-i次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:
Cmax=n(n-1)/2=O(n2)
Mmax=3n(n-1)/2=O(n2)
冒泡排序的最坏时间复杂度为O(n2)。
(3)算法的平均时间复杂度为O(n2)
虽然冒泡排序不一定要进行n-1趟,但由于它的记录移动次数较多,故平均时间性能比直接插入排序要差得多。
(4)算法稳定性
冒泡排序是就地排序,且它是稳定的。
5、算法改进
上述的冒泡排序还可做如下的改进:
(1)记住最后一次交换发生位置lastExchange的冒泡排序
在每趟扫描中,记住最后一次交换发生的位置lastExchange,(该位置之前的相邻记录均已有序)。下一趟排序开始时,R[1..lastExchange-1]是有序区,R[lastExchange..n]是无序区。这样,一趟排序可能使当前有序区扩充多个记录,从而减少排序的趟数。具体算法【参见习题】。
(2) 改变扫描方向的冒泡排序
①冒泡排序的不对称性
能一趟扫描完成排序的情况:
只有最轻的气泡位于R[n]的位置,其余的气泡均已排好序,那么也只需一趟扫描就可以完成排序。
【例】对初始关键字序列12,18,42,44,45,67,94,10就仅需一趟扫描。
需要n-1趟扫描完成排序情况:
当只有最重的气泡位于R[1]的位置,其余的气泡均已排好序时,则仍需做n-1趟扫描才能完成排序。
【例】对初始关键字序列:94,10,12,18,42,44,45,67就需七趟扫描。
②造成不对称性的原因
每趟扫描仅能使最重气泡"下沉"一个位置,因此使位于顶端的最重气泡下沉到底部时,需做n-1趟扫描。
③改进不对称性的方法
在排序过程中交替改变扫描方向,可改进不对称性。
JAVA代码:
package Utils.Sort; /** *@author Linyco *利用冒泡排序法对数组排序,数组中元素必须实现了Comparable接口。 */ public class BubbleSort implements SortStrategy { /** *对数组obj中的元素以冒泡排序算法进行排序 */ public void sort(Comparable[] obj) { if (obj == null) { throw new NullPointerException("The argument can not be null!"); } Comparable tmp; for (int i = 0 ;i < obj.length ;i++ ) { //切记,每次都要从第一个开始比。最后的不用再比。 for (int j = 0 ;j < obj.length - i - 1 ;j++ ) { //对邻接的元素进行比较,如果后面的小,就交换 if (obj[j].compareTo(obj[j + 1]) > 0) { tmp = obj[j]; obj[j] = obj[j + 1]; obj[j + 1] = tmp; } } } } }
更多用java实现冒泡排序算法相关文章请关注PHP中文网!