描述:堆排序是一種樹形選擇排序,在排序過程當中,將待排序的記錄[1...n]當作一顆徹底二叉樹的順序存儲結構,利用徹底二叉樹中雙親結點和孩子結點之間的關係,在當前無序的列表中選擇關鍵字最大(或最小)的記錄。算法
什麼是徹底二叉樹呢?徹底二叉樹就是樹中全部非終端結點的值均不大於(或不小於)其左右結點孩子的結點值。 標準的大頂堆以下數組
代碼以下:bash
function heapAdjust (arr,s,m) {
let temp = arr[s]
for (let j = 2 * s + 1;j < m;j = 2 * j + 1) {
if (j + 1 < m && arr[j] < arr[j + 1]) {
j ++
}
if (temp >= arr [j]) { // 已經爲最大堆,不須要再調整
break
}
arr[s] = arr[j]
s = j
}
arr[s] = temp
}
function swap(arr,i,j) {
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
function heapSort(arr) {
// 首先構造一個標準的大頂堆
for (let i = Math.ceil(arr.length / 2); i >= 0; i --) {
heapAdjust(arr,i,arr.length)
}
for (let j = arr.length - 1;j > 0;j --) {
swap(arr,0,j)
heapAdjust(arr,0,j)
}
}
複製代碼
思路: 將數組當作一個徹底二叉樹,首先構造一個標準的大頂堆(父節點大於孩子結點的值),只須要遍歷數組的一半長度,每次遍歷數組的,都調整堆。假設結點爲i,利用二叉樹的性質能夠知道,它的左結點爲2i+1右結點爲2i+2,遍歷數組時,將i做爲調整堆的參數,便可遍歷全部的非葉子結點。以後遍歷數組全部元素,每遍歷一次都將堆頂元素(也就是最大值)與數組最後一個元素交換,而後再調整堆繼續循環,也就是第一次循環時將最大值(堆頂元素)放到數組最後一位,調整堆[1...n-1],第二次循環,交換,調整堆[1...n-2],第三次...,直到遍歷結束ui
關鍵代碼:調整堆this
function heapAdjust (arr,s,m) {
let temp = arr[s]
for (let j = 2 * s + 1;j < m;j = 2 * j + 1) {
if (j + 1 < m && arr[j] < arr[j + 1]) {
j ++
}
if (temp >= arr [j]) { // 已經爲最大堆,不須要再調整
break
}
arr[s] = arr[j]
s = j
}
複製代碼
參數arr,原數組 參數s,數組起始座標,參數m,數組結束座標。 將起始值保存在變量temp中,開始循環,從它的左孩子結點開始遍歷,若是它的右結點存在而且右結點大於左結點,則j++,也就是求中孩子結點中較大的那個值,而後與父節點比較,若是大於父節點,則交換值,而且保存座標(下一次替換時須要用到),j=2*j+1,也就是將交換的這個結點值繼續循環,將他調整爲大頂堆,直至遍歷全部結點完成。spa
描述:LCS算法也就是求最長公共子序列(不連續),例如1,2,3,5,5和1,2,5,5,6,最長公共子序列則是2,5,5。若是連續的話則是5,5。相信你們已經明白什麼是最長公共子序列了吧。實現過程採用了算法的一種思想——動態歸化code
代碼實現以下:orm
/ LCS 求最長公共子序列
function LCS(strX,strY) {
let m = strX.length
let n = strY.length
this.lcs = function () {
let L = []
let path = []
let a,b
// 初始化二維數組
for (let i = 0; i <= m; i++) {
L[i] = []
path[i] = []
for (let j = 0; j <= n; j++) {
L[i][j] = 0
path[i][j] = 0
}
}
// 生成二維表
for (let i = 1; i <= m; i ++) {
for (let j = 1;j <= n; j ++) {
if (strX[i - 1] == strY[j - 1]) {
L[i][j] = L[i - 1][j - 1] + 1
path[i][j] = 1 // 相等爲1
} else {
a = L[i - 1][j] // 上邊
b = L[i][j - 1] // 左邊
if (b >= a) {
L[i][j] = b // 左大於上 等於0
path[i][j] = 0
} else {
L[i][j] = a
path[i][j] = -1 // 左大於上 等於-1
}
}
}
}
return {
l: L,
len: L[m][n],
path: path
}
}
// 求出最長公共子序列
this.getPath = function () {
let obj = this.lcs()
let path = obj.path
let L = obj.l
let arr = []
find(m,n,arr)
function find(i,j,arr) {
let index = path[i][j]
if (i === 0 || j === 0) {
return
}
if (index === 1) {
find(i - 1,j - 1,arr)
''
arr.push(strX[i - 1])
}
if (index === 0) {
find(i,j - 1,arr) // 往左走
}
if (index === -1) {
find(i - 1,j,arr) // 往上走
}
}
return arr
}
}
複製代碼
結構以下 最長公共子序列的結構有以下表示:cdn
設序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一個最長公共子序列Z=<z1, z2, …, zk>,則:blog
1.若xm=yn,則zk=xm=yn且Zk-1是Xm-1和Yn-1的最長公共子序列;
2.若xm≠yn且zk≠xm ,則Z是Xm-1和Y的最長公共子序列;
3.若xm≠yn且zk≠yn ,則Z是X和Yn-1的最長公共子序列。
思路:首先初始化二維數組,將全部元素都設置爲0,L數組表明長度規劃的二維數組,path數組爲路規劃路徑的數組。 初始化後表爲(也就是二維數組)
而後進行動態規劃:兩重循環遍歷數組,判斷若是strX中的值等於strY中的值,那麼L表中對應的值就等於它的上一個的值加1,,對應於表中的話也就是它的左上角(斜角),相應的path表中對應的值設置爲1若是不相等,咱們就判斷在表中它的上方的值和左邊的值大小,將L表中的值設置爲較大的那一個,同時設置path中的值,若是左邊值大於上邊的值,path表中對應的值設置爲0,不然設置爲-1(此步驟爲求解最長子序列作準備),遍歷結束後,動態規劃出來的兩個表以下: 從L表中能夠清除看到最長子序列的長度爲4接下來利用path表求出最長子序列。(採用遞歸的方式求解)
關鍵代碼:
function find(i,j,arr) {
let index = path[i][j]
if (i === 0 || j === 0) {
return
}
if (index === 1) {
find(i - 1,j - 1,arr)
arr.push(strX[i - 1])
}
if (index === 0) {
find(i,j - 1,arr) // 往左走
}
if (index === -1) {
find(i - 1,j,arr) // 往上走
}
}
複製代碼
從表中右下角開始遍歷path表,當i=0或者j=0則跳出遞歸,判斷若是邊中的值爲1,則往左上角左(即斜着走)將對應字符串中相等的值push到arr數組中,index=0;則遍歷表往左走,index=-1,則往上走(此步驟於上面設置path值相通),直到遞歸結束。 下面這個圖能夠方便理解
建議你們跟着過程一步一步畫出L表和path表,這樣走一遍基本上就拿下了這個算法。有錯之處歡迎指出,若是您以爲寫得還能夠的話那就幫我點個贊啦~ 但願能夠幫到您哈~