1. 程式人生 > >排序演算法(1):簡單選擇排序和堆排序

排序演算法(1):簡單選擇排序和堆排序

1.簡單選擇排序

(1)本質:每一趟給定待排序序列A[ 1......n ] ,選擇出第i小元素,並和A[i]交換。

程式碼:

/*************************************************
演算法:簡單選擇排序(升序)
時間複雜度為O(n^2)
**************************************************/
void simpleSelectSort(int *arr , int len)
{
	for(int i =0;i<len-1;i++)
	{
		int minIndex=i;  //輔助交換記憶體空間
		for (int j=i+1;j<len;j++)
		{
			if (arr[j]<arr[minIndex])
			{
				minIndex=j;
			}
		}
		if(minIndex != i)
			swap(&arr[i],&arr[minIndex]);
		cout<<"第"<<i+1<<"趟:";
		printArr(arr,len);
		cout<<endl;
	}
}
執行結果:



(2)效能分析

     簡單選擇排序記錄移動操作次數較少,這點優於氣泡排序。有兩層迴圈,所以時間複雜度為O(n^2)

2.堆排序

2.1 堆的定義

      n個元素的序列{k1,k2,…,kn}當且僅當滿足下列關係之一時,稱之為堆。

  情形1:ki<= k2i且ki<= k2i+1最小化堆小頂堆

  情形2:ki>= k2i且ki>= k2i+1最大化堆大頂堆

  其中i=1,2,…,n/2向下取整;

2.2 堆排序(以大頂堆為例,根節點從0開始)

     輸出堆頂最大值之後,使得剩餘n-1個元素的序列重建一個堆;如此反覆執行,便能得到一個有序序列,這個過程稱之為堆排序。(升序---大頂堆,降序---小頂堆)

(1)堆的儲存

 一般用陣列來表示堆,若根結點存在序號0處, i結點的父結點下標就為(i-1)/2。i結點的左右子結點下標分別為2*i+12*i+2

  (注:如果根結點是從1開始,則左右孩子結點分別是2i和2i+1。)

最大化堆如下:

實現堆排序需要解決兩個問題:

    1.如何由一個無序序列建成一個堆?

    2.如何在輸出堆頂元素之後,調整剩餘元素成為一個新的堆?

問題2:一般在輸出堆頂元素之後,視為將這個元素排除,然後用表中最後一個元素填補它的位置,自上向下進行調整:首先將堆頂元素和它的左右子樹的根結點進行比較,把最大的元素交換到堆頂;然後順著被破壞的路徑一路調整下去,直至葉子結點,就得到新的堆。自堆頂至葉子的調整過程為"篩選"過程。無序序列建立堆的過程就是一個反覆"篩選"過程。

帥選過程程式碼:

/****************************************************
堆排序是一種選擇排序;在最壞的情況下,其時間複雜度也為O(nlogn)
其執行時間主要耗費在建初始堆和調整建新堆時進行的反覆“篩選”上
****************************************************/
/*
函式功能:當堆的某一個節點破壞了堆的性質,呼叫此函式進行調整剩餘的元素,使其為新堆
k      :k節點破壞了堆的性質
*/
void maxHeap(int *arr,int len,int k)
{
	//遞迴邊界條件
	if (k>len/2-1)   //最後一個非葉子節點的下標為len/2-1;
	{
		return;
	}
	//1.比較k節點左右孩子的值,找出左右孩子的較大值
	int i=0;  
	//因為假設根結點的序號為0而不是1,所以i結點左孩子和右孩子分別為2i+1和2i+2
	if ((2*k+2<=len-1)&&arr[2*k+1]<arr[2*k+2])
		i=2*k+2;
	else if(2*k+1<=len-1)
		i=2*k+1;
	if (arr[k]<arr[i])
	{
		swap(&arr[k],&arr[i]);
		maxHeap(arr,len,i);
	}
}

問題1:構建初始堆

     構建堆就是對所有的非葉子節點進行篩選。最後一個非葉子節點的下標為([n/2]-1),所以篩選只需從([n/2]-1)節點開始,自下向上的進行調整,直到堆頂。

/*****************************************************************************
1.給定一個數組,首先根據該陣列元素構造一個完全二叉樹。
2.從最後一個非葉子結點開始,每次都是從父結點、左孩子、右孩子中進行比較交換;
3.交換可能會引起孩子結點不滿足堆的性質,所以每次交換之後需要重新對被交換的孩子結點進行調整。
最後一個非葉子節點下標:[n/2]-1 (根節點的下標為0), n表示節點個數
******************************************************************************/
void createHeap(int *arr , int len)
{
	//最大堆----------自下向上調整,陣列的下標從0開始
	for (int i=len/2-1;i>=0;i--)
	{
		maxHeap(arr,len,i);
	}
}

(3)堆排序

     首先輸出堆頂元素(因為它是最值),將堆頂元素和最後一個元素交換,這樣,第n個位置(即最後一個位置)作為有序區,前n-1個位置仍是無序區,對無序區進行調整,得到堆之後,再交換堆頂和最後一個元素,這樣有序區長度變為2。不斷進行此操作,將剩下的元素重新調整為堆,然後輸出堆頂元素到有序區。每次交換都導致無序區-1,有序區+1。不斷重複此過程直到有序區長度增長為n-1,排序完成。

void heapSort(int *arr , int len)
{
	//1. 構建堆
	createHeap(arr,len);
	//2.堆排序
	for (int i=0;i<len-1;i++)
	{
		//堆頂元素與最後一個元素交換
		swap(&arr[0],&arr[len-1-i]);
		//調整堆,堆頂破壞了堆的性質
		maxHeap(arr,len-i-1,0);
	}
}


(4)反思堆排序

     仔細回想一下篩選法調整堆的過程我們發現,第i次調整堆,其實就是把A中的第i大元素放到首位置A[0],然後A[0]和A[n-i-1]互換.這樣A[(n-i-1)...n]就已經有序,於是又把A[0...n-i-2]看成一個堆再進行排序,如此重複。調整堆不就是選擇出待排序序列中的最大值嗎?所以堆排序的本質和選擇排序的本質是一樣的。選擇一個待排序序列中的最小(大)值,這就是選擇排序的本質。所以,堆排序是一種選擇排序。

(5)效能分析

    堆排序時間=建堆時間+調整堆時間。從上文中知道建堆時間複雜度為O(n*log2n)。篩選法調整堆(maxHeap函式)時間O(log2n),總共迴圈了n-1次maxHeap函式,所以調整堆時間複雜度為O(n*log2n)。得出堆排序時間複雜度O(n*log2n)。且在最壞情況下時間複雜度也是O(n*log2n)。此外堆排序是不穩定的原地排序演算法。

(6)測試程式碼
void test()
{
	int arr[]={16,20,8,7,17,3,30,1,51,9,6};
	int len = sizeof(arr)/sizeof(int);
	cout<<"排序前:";
	printArr(arr,len);
	cout<<endl;

	//簡單選擇排序
	simpleSelectSort(arr,len);
	cout<<"簡單選擇排序:";
	printArr(arr,len);
	cout<<endl;

	//堆排序
	heapSort(arr , len);
	cout<<"堆排序:";
	printArr(arr,len);
	cout<<endl;
}

附件:輔助函式
void swap(int *p,int *q)
{
	int tmp;
	tmp =*p;
	*p = *q;
	*q = tmp;
}
void printArr(int *arr,int len)
{
	for (int i=0;i<len;i++)
	{
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}



相關推薦

排序演算法1簡單選擇排序排序

1.簡單選擇排序 (1)本質:每一趟從給定待排序序列A[ 1......n ] ,選擇出第i小元素,並和A[i]交換。 程式碼: /************************************************* 演算法:簡單選擇排序(升序) 時間

排序演算法1氣泡排序插入排序

1.1 氣泡排序 氣泡排序需要多次遍歷列表。它比較相鄰的項並交換那些無序的項。每次遍歷列表將下一個最大的值放在其正確的位置。實質上,每個項“冒泡”到它所屬的位置。 用python寫交換操作時,與大多數程式語言略有不同(需要臨時儲存位置),python可以執行

負載均衡演算法1簡單介紹

負載均衡(Load Balance)是分散式系統架構設計中必須考慮的因素之一,它通常是指,將請求/資料【均勻】分攤到多個操作單元上執行,負載均衡的關鍵在於【均勻】。常見網際網路分散式架構如上,分為客戶端層、反向代理nginx層、站點層、服務層、資料層。 什麼是負載均衡 負

KMP 演算法1如何理解 KMP

http://www.61mon.com/index.php/archives/183/ 系列文章目錄 KMP 演算法(1):如何理解 KMPKMP 演算法(2):其細微之處 一:背景TOC 給定一個主字串(以 S 代替)和模式串(以 P 代替),要

經典排序演算法1——氣泡排序演算法詳解

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

微信小程式入門1簡單介面的實現

原始碼我已經放在GitHub上了https://github.com/A666AHL/pupil 1.安裝 微信web開發者工具 不多BB,直接從安裝IDE開始 首先,你得進入微信公眾平臺官網(https://mp.weixin.qq.com) 點選底部的小程式並檢視詳情

PTA 資料結構題目1最大子列問題分而治之、線上處理演算法

題目來源: 問題描述: 問題分析: 對於一般的問題,原始解 都能通過一種 蠻力演算法,即窮舉法的思想得到。這題也不例外。 如果我們,把輸入的陣列,所有的子列都歷遍,並從中找出最大,即可得出我們的演算法。也就是版本一。 學習要點: 1、如何

Caffe學習筆記1簡單的資料視覺化

caffe的底層是c++寫的,如果要進行資料視覺化,需要藉助其它的庫或者是介面,如opencv,python或者是matlab,python的環境需要自行配置,因為我使用的都是網管同志已經配置好的深度學習伺服器,所以不用管底層的一些配置問題,如果需要自行配置自己

python實戰1簡單的資料採集與分析

最近忙著做畢業設計,最愛的python當然成了我的切入點。因此特意找了一個專案來練練手,專案很簡單,就是利用python爬取資料,然後再利用matplotlib進行資料視覺化。 專案設計:爬蟲爬取資料並存入mongodb資料庫中,然後再編寫指令碼讀取資料,進行

排序演算法1插入排序演算法分析

導語 今天,我們介紹的是排序演算法經典的一種排序演算法,這個演算法是插入排序。 相信大家都玩過紙牌。插入排序的工作方式就像許多人排序一手撲克牌。 開始時,我們的左手為空並且桌子上的牌面朝下(意味著我們不在翻開之前並不知道下一張牌是多大的)

Python1簡單影象處理圖片->二進位制->圖片

#coding=utf-8 ''' 1-將圖片轉化為陣列並存為二進位制檔案 2-從二進位制檔案中讀取數並重新恢復為圖片 ''' from __future__ import print_funct

排序演算法計數排序與桶排序

插入排序、堆排序、歸併排序等排序方法,在排序的最終結果中,各個元素的次序依賴於他們之間的比較,我們把這一類的排序演算法稱為比較排序。在最壞情況下,任何比較排序演算法都要經過 Omega(nlgn)次比較。因此堆排序和歸併排序都是漸近最優的比較排序演算法。

排序演算法插入排序排序

排序演算法是演算法研究中最基礎的問題,本文針對排序演算法,介紹幾種排序演算法的基本方法、時間複雜度及Java實現等內容。 原址性 :如果輸入陣列中僅有常數個元素需要在排序的過程中儲存在陣列之外,那麼排序演算法就是原址的。         常用排序演算法的執行

35. 排序演算法8歸併排序的迭代實現

1. 基本原理   在上篇文章中介紹了歸併排序的遞迴實現,雖然遞迴的實現方式很簡單,通過遞迴呼叫就可以實現,但是會佔用大量的時間和空間,使得演算法的效率下降;使用迭代的方式代替遞迴的方式雖然會使得程式碼的編寫變得困難,但是會增大效率。   遞迴的思想實際上是

【H.264/AVC視訊編解碼技術詳解】七、 熵編碼演算法1基礎知識

《H.264/AVC視訊編解碼技術詳解》視訊教程已經在“CSDN學院”上線,視訊中詳述了H.264的背景、標準協議和實現,並通過一個實戰工程的形式對H.264的標準進行解析和實現,歡迎觀看! “紙上得來終覺淺,絕知此事要躬行”,只有自己按照標準文件以程式碼

演算法君帶你學演算法1最長迴文字串

演算法君:小白同學,給你出道演算法題,看你小子演算法能力有沒有長進。 演算法小白:最近一直在研究演算法,刷了很多演算法題,正好活動活動大腦,來來來,趕快出題! 演算法君:聽好了,題目是:求一個字串中最長的迴文字串。 演算法小白:這個演算法好像很簡單,就是有一個概念不太明白,啥叫“迴文字串&r

C++學習1最大子段多種解法

多少 問題: code namespace 數據 組成 amp using () 問題:給定由n個數(可能為負數)組成的序列a1,a2,a3,...,an,求該序列子段和的最大值。 第一種解法:(最容易考慮的方法,將所有的子段一一相加,然後比較) 1 #include&

基礎知識概念1Socket 長連線短連線的概念

1.短連線 連線->傳輸資料->關閉連線     HTTP是無狀態的,瀏覽器和伺服器每進行一次HTTP操作,就建立一次連線,但任務結束後就中斷連線。短連線是指SOCKET建立連線後 ,傳送後或接收完資料後,就馬上斷開連線。 2.長連線

多執行緒1繼承Thread類實現Runnable介面

多執行緒的兩種實現方法: 1.繼承Thread類     繼承Thread類,重寫run()方法。建立多執行緒的時候,需要建立物件例項,然後呼叫start()方法。類物件的屬性屬於執行緒私有,執行緒之間互不影響。 public class ClassExtendT

機器學習儲備1協方差相關係數

為了深刻理解機器學習演算法的原理,首先得掌握其中涉及到的一些基本概念和理論,比如概率,期望,標準差,方差。在這些基本概念上,又衍生出了很多重要概念,比如協方差,相關係數等。今天我們就來聊聊這些組成機器學習的基本概念。 1、概率 概率 P 是對隨機事件發生的可能性的度量。 例如,小明在期末