leetcode初級算法(數組篇)

leetcode初級算法(數組篇)

注:點標題能夠查看對應的題目,本文代碼均使用Javascript編寫。本文代碼不必定是最優解,僅供參考,如仁兄有更好的實現,歡迎交流。javascript

刪除排序數組中的重複項

解析:首先要注意關鍵詞,排序數組原地不須要考慮數組中超出新長度後面的元素。有這些已知條件,解決起來就簡單不少了。java

思路:git

  1. 遍歷數組,使用一個臨時遍歷存儲新的不重複的值;
  2. 當數組中有不一樣的值出現時,更新數組、臨時值,並給計數變量加1;
  3. 最後返回不重複的數據的數量。

代碼:es6

/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    // 若是數組爲空,直接返回0
    if(nums.length === 0){
        return 0;
    }
    
    var length = 1; // 不重複值的數量,只要數組不爲空,至少有一個值是不重複的,因此初始值爲1
    var temp = nums[0]; // 存儲新的不重複的值
    
    for(var i = 1; i < nums.length; i++){
        if(nums[i] !== temp){
            temp = nums[i]; // 更新不重複的值
            nums[length] = nums[i]; // 更新原數組
            length++; // 更新下標
        }
    }
    
    return length; // 返回不重複數據數量
};

買賣股票的最佳時機 II

解析: 這裏使用了貪心算法思想,貪心算法簡單來講就是:局部最優解達成全局最優解。只關心局部最優的解決方法,累加局部最優的的解產生的就是全局最優解。就是把一個複雜的問題拆分紅不少相同的小問題,解決小問題後,大問題也就解決了。使用貪心算法的條件是:局部最優策略能產生全局最優解。有時候局部最優解不一能產生全局最優解,因此在使用以前須要分析。本題獲取最大利潤全局解局部最優解只關心相鄰兩天,只要今天股票比昨天股票價高股就進行交易算法

思路:數組

  1. 定義一個變量存儲收益總和;
  2. 遍歷數組,計算今天減昨天的收益,累加;
  3. 返回收益總和。

代碼:spa

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    if(!prices || prices.length <= 0){
        return;
    }
    
    var maxProfit = 0; // 收益總和
    for(var i = 0; i < prices.length - 1; i++){
       // 今天股價比昨天股價高,則進行一次買賣操做
       if(prices[i] < prices[i + 1]){
           maxProfit += prices[i + 1] - prices[i]; // 計算相鄰兩天股票差價的收益
       }
    }
    
    return maxProfit;
};

旋轉數組

解析:這裏k是非負數,不用考慮向左旋轉的狀況。向右旋轉k步就是剪切數組末尾的k項貼到數組頭部。旋轉步數和數組長度同樣時至關於沒有變化。code

代碼:對象

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var rotate = function(nums, k) {
    var len = nums.length;
    k = k % len; // 求餘數,防止k大於數組長度
    var temp1 = nums.splice(len - k, k); // 獲取數組末尾的k項
    nums.unshift(...temp1); // 拼接到原數組的頭部
};

存在重複元素

解析:利用對象記錄當前值是否已存在,對象的屬性名爲當前值。blog

代碼:

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var containsDuplicate = function(nums) {
    var object = {};
    for(var i = 0; i < nums.length; i++){
        var cur = nums[i];
        // 當前值存在 返回true
        if(object[cur]){
            return true;
        }else{
            // 當前值不存在,記錄一下
            object[cur] = true
        }
    }
    return false; // 遍歷完整個數組沒有發現重複的值,則返回false
};

只出現一次的數字

解析:使用位運算——異或。異或:相同取0,不一樣取1。

例:3 ^ 4,3的二進制位爲:011,4的二進制爲100,3異或4得出:111,轉回十進制也就是:7。

思路:利用相同的數異或爲0,任何數與0進行異或等於它自己的規則進行查找。這裏實現上使用的是es6reduce方法。

代碼:

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    return nums.reduce((prev, cur) => (prev ^ cur), 0); // 將0與第一位的數進行異或,得出的結果再與下一位數進行異或
};

兩個數組的交集 II

解析:求兩個數組的交集,最簡單粗暴的方法就是依次比較兩個數組的值是否相等。這道題看似簡單,實際上網上不少答案都是錯的,他們沒有考慮到比較重複值的狀況。

代碼:

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersect = function (nums1, nums2) {
    var result = []; // 交集
    var comparedIndex = []; // 記錄比較過的索引值
    for (var i = 0; i < nums1.length; i++) {
        for (var j = 0; j < nums2.length; j++) {
            // 找出相同的而且還未加入結果的值
            if (nums1[i] === nums2[j] && comparedIndex.indexOf(j) === -1) {
                result.push(nums1[i]); // 加入相同的值
                comparedIndex.push(j); // 記錄當前索引值
                break; // 找到一個相同的則終止循環體,無需比較後續的值
            }
        }
    }
    return result;
};

也可使用es6filter方法:過濾出num1中在num2中也存在的數據。

代碼:

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersect = function (nums1, nums2) {
    const nums2Copy = [...nums2]; // 建一個nums2的備份
    return nums1.filter(ele => {
        const index = nums2Copy.indexOf(ele); // 查看nums2Copy中是否存與當前值相同
        if (index !== -1) { // 若是找獲得
            nums2Copy.splice(index, 1); // 在nums2Copy中刪除找過的值
            return true; // 返回當前值
        }
    });
};

加一

解析:先將數組轉化爲數字,再將數字加一,而後再轉回爲數組。

思路:

  1. 數組裝字符串;
  2. 字符串裝數字;
  3. 數字加1;
  4. 數字裝字符串;
  5. 字符串轉數組;

注意:字符串過長會致使轉換成數字類型時精度缺失,因此這裏要使用es6的新數據類型BigInt

代碼:

/**
 * @param {number[]} digits
 * @return {number[]}
 */
var plusOne = function(digits) {
    var result = digits.join(''); // 數組轉字符串
    result = BigInt(result); // 字符串轉BigInt
    result += BigInt(1); // 加1
    result = result.toString().split(''); // 轉換回數組
    return result;
};

解法二:使用循環,遍歷數組,滿10進1。

代碼:

/**
 * @param {number[]} digits
 * @return {number[]}
 */
var plusOne = function(digits) {
    const result = [...digits]; // 備份數組
    const end = result.length - 1;
    for(let i = end; i >= 0; i--){
        if(result[i] === 9){ // 當前位置的數爲9時
            result[i] = 0; // 當前數加一後就變爲0
            if(i - 1 >= 0){ // 沒有超過數組長度,直接進行後續循環
              continue;
            }else{
                result.unshift(1); // 超過數組元長度,頭部加一位數字1
                break;
            }
        }else{
            result[i] += 1; // 當前位置數字不爲9,直接加1
            break; // 不發生進位,跳出循環
        }
    }
    return result;
};

移動零

解析:遍歷數組,使用一個遍歷記錄0的數量,當遇到一個0的時候,記錄0的個數,遇到不是0的時候,將當前數的值往前移動與當前0的數量相同距離。並將當前值變爲0。

代碼:

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var moveZeroes = function(nums) {
    if(nums.length <= 1){ // 數組小於1位,直接返回
        return;
    }
    var zeroNums = 0; // 當前0的個數
    for(var i = 0; i < nums.length; i++){
        if(nums[i] === 0){ // 當前值爲0時
            zeroNums += 1; // 0的個數加1
        }else if(zeroNums > 0){ // 0的數量不爲0時
            nums[i - zeroNums] = nums[i]; // 將當前值往前移動zeroNums位
            nums[i] = 0; // 將當前值變爲0
        }
    }
};

兩數之和

解析:使用雙重循環,每次計算當前索引的值和剩下索引的值的和相加是否爲目標值,是則返回相加的兩個索引值。

代碼:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    var len = nums.length;
    for(var i = 0; i < len; i++){
        for(var j = i + 1; j < len; j++){
            if(nums[i] + nums[j] === target){
                return [i, j];
            }
        }
    }
};

有效的數獨

解析:二維數組,雙重循環是少不了的。校驗的規則很明確有三點,如今有的已知條件是循環是的兩個下標ij,要根據已知條件完成校驗規則的校驗。

思路:

  1. 數字 1-9 在每一行只能出現一次。

    這個其實和以前的數組去重是一個原理,用一個對象記錄一個值是否已經存在,若是存在,第二次在遇到這個值就說明校驗不經過。

  2. 數字 1-9 在每一列只能出現一次。

    列和行的判斷其實同樣,只須要將索引交換便可,例:arr[0][2]表明第1行的第3個數,那麼arr[2][0]就表明第1列第三個數。

  3. 數字 1-9 在每個以粗實線分隔的 3x3 宮內只能出現一次。

    這裏要找規律,宮有9個,行和列表也有9個,若是能找到每一個宮的下標和行列下標(也就是ij)的關係,就能夠循環行的時候對9個宮進行重複數據判斷了。這裏的規律要靠不斷嘗試試出來的,對數字敏感的人想的可能快一下,沒有什麼特定的套路,話很少說,直接上規律:

    • 宮的行座標 = 3 * (i / 3) + j / 3;
    • 宮的列座標 = 3 * (i % 3) + j % 3;
    • 宮與行的關係:第i行表明第i+1個宮;

image-20200611163455966.png

代碼:

/**
 * @param {character[][]} board
 * @return {boolean}
 */
var isValidSudoku = function (board) {
    for (var i = 0; i < 9; i++) {
        var rowShowOnce = {}; // 校驗行是否無重複項
        var colShowOnce = {}; // 校驗列是否無重複項
     var squareShowOnce = {}; // 校驗宮是否無重複項
        for (var j = 0; j < 9; j++) {
            var row = board[i][j]; // 行的值
            var col = board[j][i]; // 列的值
            var squareX = 3 * Math.floor(i / 3) + Math.floor(j / 3); // 宮的行座標
            var squareY = 3 * (i % 3) + j % 3; // 宮的列座標
            var square = board[squareX][squareY]; // 宮的值
             // 判斷當前行值在同一行中是否存在   
            if (row !== '.') {
                if (rowShowOnce[row]) {
                    return false; // 已存在,行校驗不過,返回false
                } else {
                    rowShowOnce[row] = true;
                }
            }
            // 判斷當前列值在同一列中是否存在
            if (col !== '.') {
                if (colShowOnce[col]) {
                    return false; // 已存在,列校驗不過,返回false
                } else {
                    colShowOnce[col] = true;
                }
            }
            // 判斷當前宮值在同一個宮中是否存在
            if (square !== '.') {
                if (squareShowOnce[square]) {
                    return false; // 已存在,宮校驗不過,返回false
                } else {
                    squareShowOnce[square] = true;
                }
            }
        }
    }
    return true;
};

旋轉圖像

解析:這種題通常將變化狀況列舉出來,而後找下標變化規律就好。

image-20200612100912934.png

第一行:

原下標 新下標
0,0 0,2 1
0,1 1,2 2
0,2 2,2 3

第二行:

原下標 新下標
1,0 0,1 4
1,1 1,1 5
1,2 2,1 6

第三行:

原下標 新下標
2,0 0,0 7
2,1 1,0 8
2,2 2,0 9

不難看出:

  • 新的行下標 = 舊的列座標
  • 新的列座標 = n - 1 - 舊的行座標

可是,這裏題目要求在原地旋轉,當前規律適用於建一個新的矩陣,因此還要想一想其它辦法。

分析:爲達到原地旋轉,每次調整就得一次到位,矩形四條邊,一次調整四個數據的位置。

image-20200612115213756.png

第一次調整:

步驟 原下標 新下標
1 0,0 0,2 1
2 0,2 2,2 3
3 2,2 2,0 9
4 2,0 0,0 7

第二次調整:

步驟 原下標 新下標
1 0,1 1,2 4
2 1,2 2,1 2
3 2,1 1,0 6
4 1,0 0,1 8

代碼:

/**
 * @param {number[][]} matrix
 * @return {void} Do not return anything, modify matrix in-place instead.
 */
var rotate = function(matrix) {
    var n = matrix.length;
    for(var i = 0; i < n / 2; i++){
        for(var j = i; j < n - 1 - i; j++){
            // 交換兩個值
            var temp = matrix[i][j];
            matrix[i][j]=matrix[n-1-j][i]; // 步驟4
            matrix[n-1-j][i]=matrix[n-1-i][n-1-j]; // 步驟3
            matrix[n-1-i][n-1-j]=matrix[j][n-1-i]; // 步驟2
            matrix[j][n-1-i]=temp; // 步驟1
        }
    }
};
相關文章
相關標籤/搜索