JavaScript數據結構與算法-Sort-(leetcode原題)

排序

  • 時間複雜度(運行次數)

咱們假設計算機運行一行基礎代碼須要執行一次運算。javascript

int aFunc(void) {
    printf("Hello, World!\n");      //  須要執行 1 次
    return 0;       // 須要執行 1 次
}

那麼上面這個方法須要執行 2 次運算前端

int aFunc(int n) {
    for(int i = 0; i<n; i++) {         // 須要執行 (n + 1) 次
        printf("Hello, World!\n");      // 須要執行 n 次
    }
    return 0;       // 須要執行 1 次
}

這個方法須要 (n + 1 + n + 1) = 2n + 2 次運算。
咱們把 算法須要執行的運算次數 用 輸入大小n 的函數 表示,即 T(n) 。java

經常使用算法時間複雜度:算法

  • O(1)常數型
  • O(n)線性型
  • O(n^2)平方型
  • O(n^3)立方型
  • O(2^n)指數型
  • O(log2^n)對數型
  • O(nlog2^n)二維型

時間複雜度的分析方法:
一、時間複雜度就是函數中基本操做所執行的次數
二、通常默認的是最壞時間複雜度,即分析最壞狀況下所能執行的次數
三、忽略掉常數項
四、關注運行時間的增加趨勢,關注函數式中增加最快的表達式,忽略係數
五、計算時間複雜度是估算隨着n的增加函數執行次數的增加趨勢
六、遞歸算法的時間複雜度爲:遞歸總次數 * 每次遞歸中基本操做所執行的次數segmentfault

  • 空間複雜度(佔用內存)
  1. 算法消耗的空間

一個算法的佔用空間是指算法實際佔用的輔助空間總和數組

  1. 算法的空間複雜度
    算法的空間複雜度不計算實際佔用的空間,而是算整個算法的「輔助空間單元的個數」。算法的空間複雜度S(n)定義爲該算法所耗費空間的數量級,它是問題規模n的函數。記做:
S(n)=O(f(n)) 1

若算法執行時所須要的輔助空間相對於輸入數據量n而言是一個常數,則稱這個算法的輔助空間爲O(1);
遞歸算法的空間複雜度:遞歸深度N*每次遞歸所要的輔助空間, 若是每次遞歸所需的輔助空間是常數,則遞歸的空間複雜度是 O(N).函數

冒泡排序

原理:從第一個元素開始,日後比較,遇到本身小的元素就交換位置
let arr = [89, 19, 90, 9, 3, 21, 5, 77, 10, 22]

function bubbleSort(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
      }
    }
  }
  return arr;
}
bubbleSort(arr)

for (let j = 0; j < arr.length - 1 - i; j++)對於這裏的理解:性能

  1. i = 0 時, j最大值arr.length-2,那最後一個值就不比嗎?並非,if (arr[j] > arr[j + 1])若是j<arr.length,j+1就會溢出。
  2. 那爲何又要-i呢,當i=0時,通過第一次循環,最大值就會放到數組的最後一位,此時,在進行第二次循環的時候i=1,最後的最大數就不必再比了,要比的就是前length-1-1項,以此類推,能夠減小循環次數,控制時間複雜度,因此j < arr.length - 1 - i
// 另外一種寫法
let arr = [89, 19, 90, 9, 3, 21, 5, 77, 10, 22]

function bubbleSort(arr) {
// 用i來作邊界最大值
  for (let i = arr.length - 1 ; i > 0 ; i--) {
    for (let j = 0; j < i; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
      }
    }
  }
  return arr;
}
bubbleSort(arr)

選擇排序

它的工做原理以下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。以此類推,直到全部元素均排序完畢。
let arr = [89, 19, 90, 9, 3, 21, 5, 77, 10, 22]
function selectionSort(arr) {
  let len = arr.length;
  let min = ''; // 定一個最小值
  // i < len-1 * 由於j = i + 1,否則會重複比較一次最後一位
  for (let i = 0 ; i < len-1 ; i++) {
      min = i
    for (let j = i+1; j < len; j++) {
      if (arr[min] > arr[j]) {
        min = j
      }
    }
    [arr[i], arr[min]] = [arr[min], arr[i]]
    console.log(`i=${i}; min=${min}; arr=${arr}`)
  }
  return arr;
}
selectionSort(arr)
// 循環過程
i=0; min=4; arr=3,19,90,9,89,21,5,77,10,22
i=1; min=6; arr=3,5,90,9,89,21,19,77,10,22
i=2; min=3; arr=3,5,9,90,89,21,19,77,10,22
i=3; min=8; arr=3,5,9,10,89,21,19,77,90,22
i=4; min=6; arr=3,5,9,10,19,21,89,77,90,22
i=5; min=5; arr=3,5,9,10,19,21,89,77,90,22
i=6; min=9; arr=3,5,9,10,19,21,22,77,90,89
i=7; min=7; arr=3,5,9,10,19,21,22,77,90,89
i=8; min=9; arr=3,5,9,10,19,21,22,77,89,90

最大間距

給定一個無序的數組,找出數組在排序以後,相鄰元素之間最大的差值。

若是數組元素個數小於 2,則返回 0。

示例 1:

輸入: [3,6,9,1]
輸出: 3
解釋: 排序後的數組是 [1,3,6,9], 其中相鄰元素 (3,6) 和 (6,9) 之間都存在最大差值 3。
示例 2:

輸入: [10]
輸出: 0
解釋: 數組元素個數小於 2,所以返回 0。
說明:

你能夠假設數組中全部元素都是非負整數,且數值在 32 位有符號整數範圍內。
請嘗試在線性時間複雜度和空間複雜度的條件下解決此問題。
var maximumGap = function(nums) {
    //if (nums.length < 2) {
        //return 0;
  //  }
    //nums.sort((a,b) =>  a-b)
    //let max = 0;
    //for(let i = 0; i< nums.length-1; i++) {
        //max = nums[i+1]-nums[i]>max?nums[i+1]-nums[i]:max
    //}
    //return max;
    if (nums.length < 2) {
        return 0;
    }
    nums.sort((a,b) =>  a-b)
    let max = 0,grap;
    for(let i = 0; i< nums.length-1; i++) {
        grap = nums[i+1]-nums[i]
        max = grap>max?grap:max
    }
    return max;
};
// leetcode上的優解
/**
 * @param {number[]} nums
 * @return {number}
 */
var maximumGap = function (nums) {
  if (nums.length < 2) return 0
  let max = nums[0], min = nums[0]
  for (let i = 1; i < nums.length; i++) {
    max = Math.max(nums[i], max)
    min = Math.min(nums[i], min)
  }
  let delta = (max - min) / (nums.length - 1)
  let maxBucket = new Array(nums.length - 1).fill(Number.MIN_SAFE_INTEGER)
  let minBucket = new Array(nums.length - 1).fill(Number.MAX_SAFE_INTEGER)
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] == min || nums[i] == max) continue
    let index = Math.floor((nums[i] - min) / delta)
    maxBucket[index] = Math.max(maxBucket[index], nums[i])
    minBucket[index] = Math.min(minBucket[index], nums[i])
  }
  let prev = min, maxGap = 0
  for (let i = 0; i < minBucket.length; i++) {
    if (minBucket[i] == Number.MAX_SAFE_INTEGER) continue
    maxGap = Math.max(minBucket[i] - prev, maxGap)
    prev = maxBucket[i]
  }
  maxGap = Math.max(max - prev, maxGap)
  return maxGap
};

// 輸入 [3,6,9,1]
// 最大值 9,最小值 1
// 最大桶 [-∞,-∞,-∞] 注意是反的,長度比原數組少1
// 最小桶 [+∞,+∞,+∞] 注意是反的,長度比原數組少1
// 平均桶間距 (9-1)/4 = 2
// 把值逐個放到桶 (nums[i]-最小值)/平均間距
// (3 - 1)/2 = 1 ,修改最小桶座標1爲3, [+∞,3,+∞],同理最大桶 [-∞,3,-∞]
// (6 - 1)/2 = 2.5 = 2, 最小桶 [+∞,3,6] 最大桶 [-∞,3,6]
// 9 爲最大值,跳過
// 1 爲最小值,跳過
// 若是有落在同一個桶的則最大桶取最大值,最小桶取最小值,此例子中沒有重複落入狀況
// 從最小桶找到間隔最大的座標 最小值=1,最小桶 [+∞,3,6],最大桶[-∞,3,6] 最大值=9
// 即較大間隔有3段,1-3(最小桶),3(最大桶)-6(最小桶),6(最大桶)-9
// 間隔 2,3,3 取最大 3

按奇偶排序數組

給定一個非負整數數組 A,返回一個數組,在該數組中, A 的全部偶數元素以後跟着全部奇數元素。

你能夠返回知足此條件的任何數組做爲答案。

 

示例:

輸入:[3,1,2,4]
輸出:[2,4,3,1]
輸出 [4,2,3,1],[2,4,1,3] 和 [4,2,1,3] 也會被接受。
 

提示:

1 <= A.length <= 5000
0 <= A[i] <= 5000
var sortArrayByParity = function(A) {
    let arr = []
    for(let i = 0;i<A.length;i++) {
        if(A[i]%2 == 0) {
            arr.unshift(A[i])
        } else {
            arr.push(A[i])
        }
    }
    return arr;
};

按奇偶排序數組II

給定一個非負整數數組 A, A 中一半整數是奇數,一半整數是偶數。

對數組進行排序,以便當 A[i] 爲奇數時,i 也是奇數;當 A[i] 爲偶數時, i 也是偶數。

你能夠返回任何知足上述條件的數組做爲答案。

示例:

輸入:[4,2,5,7]
輸出:[4,5,2,7]
解釋:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也會被接受。
 

提示:

2 <= A.length <= 20000
A.length % 2 == 0
0 <= A[i] <= 1000

思路:利用雙指針,每次+2學習

var sortArrayByParityII = function(A) {
    let i = 0;
    let j = 1;
    while (j < A.length && i < A.length) {
        if (A[i] % 2 == 0) {
            i += 2;
        } else {
            while (A[j] % 2 != 0 && j < A.length) {
                j += 2;
            }
            if (j < A.length) {
                let tmp = A[i]
                A[i] = A[j]
                A[j] = tmp
            }
        }
    }
    return A;
};

缺失的第一個正數

給定一個未排序的整數數組,找出其中沒有出現的最小的正整數。

示例 1:

輸入: [1,2,0]
輸出: 3
示例 2:

輸入: [3,4,-1,1]
輸出: 2
示例 3:

輸入: [7,8,9,11,12]
輸出: 1
說明:

你的算法的時間複雜度應爲O(n),而且只能使用常數級別的空間。
// 第一種解法
function firstMissingPositive(arr) {
    // 過濾到非正數
    arr = arr.filter(item => item > 0);

    if(arr.length ==0) {
        // 數組爲空說明沒有正數,那最小的正數就是1
        return 1;
    } else {
        // 排序
        arr.sort((a,b) => a-b);
        // 若是第一項不是1,那就返回1
        if(arr[0] !== 1) {
            return 1
        } else {
            for (let i = 0,len = arr.length; i < len; i++) {
                if(arr[i+1] - arr[i] > 1) {
                    return arr[i] + 1
                } 
            }
            // 若是上面沒有return,那就返回數組最後一項 + 1
            return arr.pop() + 1
        }
    }
};
// 利用選擇排序優化代碼性能,上面那種寫法,最大的缺點就是對全部數據都進行了排序
function firstMissingPositive(arr) {
    arr = arr.filter(item => item > 0);

    // 選擇排序,先拿到最小值,若是第一個元素不是1就返回1
    let min = 0;
    let len = arr.length;
    for (let i = 0; i < len; i++) {
        min = i;
        for (let j = i+1; j < len; j++) {
            if (arr[min] > arr[j]) {
                min = j
            }
        }
        [arr[i], arr[min]] = [arr[min], arr[i]]
        // 當進行到第二次遍歷後,就能夠比較了
        if (i>0) {
            if(arr[i]-arr[i-1]>1) {
                return arr[i-1] + 1
            }
        } else {
            // 若是第一項最小正數不是1,就返回1 
            if (arr[0]!==1){
                return 1;
            }
        }
    }
    // 上面的狀況都沒經過,這也是最壞的狀況,就判斷數組的長度若是爲0就返回1,反之返回數組最後一項+1
    return arr.length?arr.pop() + 1:1
}

最後

建立了一個前端學習交流羣,感興趣的朋友,一塊兒來嗨呀!
優化

相關文章
相關標籤/搜索