刷leetCode算法題+解析(十六)

旋轉數(shù)組

題目:給定一個數(shù)組,將數(shù)組中的元素向右移動 k 個位置哀卫,其中 k 是非負數(shù)。

示例 1:
輸入: [1,2,3,4,5,6,7] 和 k = 3
輸出: [5,6,7,1,2,3,4]
解釋:
向右旋轉 1 步: [7,1,2,3,4,5,6]
向右旋轉 2 步: [6,7,1,2,3,4,5]
向右旋轉 3 步: [5,6,7,1,2,3,4]
示例 2:
輸入: [-1,-100,3,99] 和 k = 2
輸出: [3,99,-1,-100]
解釋:
向右旋轉 1 步: [99,-1,-100,3]
向右旋轉 2 步: [3,99,-1,-100]
說明:
盡可能想出更多的解決方案撬槽,至少有三種不同的方法可以解決這個問題此改。
要求使用空間復雜度為 O(1) 的 原地 算法。

思路:這個題乍一看讓我想起了樹的左旋右旋啊侄柔。不過這個題沒有這么復雜其實共啃,仔細看題就能發(fā)現(xiàn)所謂的右旋就是最右(后)位元素換到前面來了。不考慮性能的話暴力法還是很容易做到的暂题。不管是挨個元素改變值移剪,還是直接數(shù)組復制串塑,反正還是那句話嘹履,實現(xiàn)簡單谒养,優(yōu)化難呀舔。而且這個空間復雜度為O(1)也就是不額外建立數(shù)組僻爽,沒要求時間復雜度。有點思路了庭惜,我先去擼代碼
第一版本出爐:就是最暴力的解法这揣,雖然滿足空間復雜度,但是性能一言難盡怀吻。我再慢慢優(yōu)化:

 public void rotate(int[] nums, int k) {
        for(int i = 0;i<k;i++){
            int last = nums[nums.length-1];
            for(int j=nums.length-1;j>0;j--){
                nums[j] = nums[j-1];
            }
            nums[0] = last;
        }
    }

第二種方法就是數(shù)組倒轉瞬浓。用雙指針的方法將數(shù)組倒轉封裝起來。然后這個
其實很容易理解蓬坡,整個數(shù)組倒轉猿棉。然后在k之前(包括k位)的再到轉,k之后的倒轉屑咳。
單純這么說可能很抽象萨赁,畫個圖就好理解了。


三次倒轉獲得正確數(shù)據(jù)流程

反正情況就是這個情況乔宿,盡量腦補位迂,接下來上代碼:

class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        k %= n;
        reverse(nums, 0, n - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, n - 1);
    }
    private void reverse(int[] nums, int first, int last) {
        while (first < last) {
            int temp = nums[first];
            nums[first++] = nums[last];
            nums[last--] = temp;
        }
    }
}

然后雖然題目要求三種方式,但是我覺得上種方法性能详瑞,時間已經很優(yōu)了掂林,所以就不再執(zhí)著于方式了,這個題就到這里坝橡。如果感興趣或者必須按照題目做出來的建議去看解析吧泻帮。附上大神提供的完整四種方法的思路。

import java.util.Arrays;

class Solution {
    /**
     * 雙重循環(huán)
     * 時間復雜度:O(kn)
     * 空間復雜度:O(1)
     */
    public void rotate_1(int[] nums, int k) {
        int n = nums.length;
        k %= n;
        for (int i = 0; i < k; i++) {
            int temp = nums[n - 1];
            for (int j = n - 1; j > 0; j--) {
                nums[j] = nums[j - 1];
            }
            nums[0] = temp;
        }
    }

    /**
     * 翻轉
     * 時間復雜度:O(n)
     * 空間復雜度:O(1)
     */
    public void rotate_2(int[] nums, int k) {
        int n = nums.length;
        k %= n;
        reverse(nums, 0, n - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, n - 1);
    }


    private void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start++] = nums[end];
            nums[end--] = temp;
        }
    }

    /**
     * 循環(huán)交換
     * 時間復雜度:O(n)
     * 空間復雜度:O(1)
     */
    public void rotate_3(int[] nums, int k) {
        int n = nums.length;
        k %= n;
        // 第一次交換完畢后计寇,前 k 位數(shù)字位置正確锣杂,后 n-k 位數(shù)字中最后 k 位數(shù)字順序錯誤,繼續(xù)交換
        for (int start = 0; start < nums.length && k != 0; n -= k, start += k, k %= n) {
            for (int i = 0; i < k; i++) {
                swap(nums, start + i, nums.length - k + i);
            }
        }
    }

    /**
     * 遞歸交換
     * 時間復雜度:O(n)
     * 空間復雜度:O(n/k)
     */
    public void rotate(int[] nums, int k) {
        // 原理同上
        recursiveSwap(nums, k, 0, nums.length);
    }

    private void recursiveSwap(int[] nums, int k, int start, int length) {
        k %= length;
        if (k != 0) {
            for (int i = 0; i < k; i++) {
                swap(nums, start + i, nums.length - k + i);
            }
            recursiveSwap(nums, k, start + k, length - k);
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

顛倒二進制位

題目:顛倒給定的 32 位無符號整數(shù)的二進制位番宁。

示例 1:
輸入: 00000010100101000001111010011100
輸出: 00111001011110000010100101000000
解釋: 輸入的二進制串 00000010100101000001111010011100 表示無符號整數(shù) 43261596元莫,
因此返回 964176192,其二進制表示形式為 00111001011110000010100101000000蝶押。
示例 2:
輸入:11111111111111111111111111111101
輸出:10111111111111111111111111111111
解釋:輸入的二進制串 11111111111111111111111111111101 表示無符號整數(shù) 4294967293踱蠢,
因此返回 3221225471 其二進制表示形式為 10101111110010110010011101101001。
提示:
請注意棋电,在某些語言(如 Java)中茎截,沒有無符號整數(shù)類型。在這種情況下赶盔,輸入和輸出都將被指定為有符號整數(shù)類型企锌,并且不應影響您的實現(xiàn),因為無論整數(shù)是有符號的還是無符號的于未,其內部的二進制表示形式都是相同的撕攒。
在 Java 中陡鹃,編譯器使用二進制補碼記法來表示有符號整數(shù)。因此打却,在上面的 示例 2 中杉适,輸入表示有符號整數(shù) -3,輸出表示有符號整數(shù) -1073741825柳击。
進階:
如果多次調用這個函數(shù)猿推,你將如何優(yōu)化你的算法?

思路:這個題捌肴。蹬叭。我不知道是不是我沒太理解,不就是將以此二進制串當做字符串然后完全倒轉么状知?我再仔細審審題然后再來分享思路秽五。
回來了,講一下這道題的幾個坑:1饥悴,說是輸入二進制串坦喘,實際上輸入的是int數(shù)。二西设,有個負數(shù)補碼問題瓣铣。我一開始的思路就卡在負數(shù)這一塊了。
雖然我第一個思路是錯的贷揽,但是這里也貼來上棠笑,知道為什么錯才更好了解為什么對:

    public int reverseBits(int n) {
        StringBuffer sb = new StringBuffer();
        int i = 32;
        //因為是n是int,所以肯定是n先等于0,所以只要判斷i就可以了
        while(i!=0 && n>=0){
            sb.append(n%2);
            n = n/2;
            i--;
        }
        return Long.valueOf(sb.toString(),2).intValue();
    }

這個就坑在如果是正數(shù)都ok禽绪,但是如果初始數(shù)字是負數(shù)則會報錯蓖救。我是手動判斷二進制位數(shù)的,初始是-1,然后還往sb上追加得出的結果根本不能看做是數(shù)字印屁。所以這種生生拆數(shù)的方式就不行循捺。
還能怎么獲取二進制位數(shù)呢?位移雄人。一種知道但是很少用的技術巨柒。
這里科普幾個知識點:
按位與&。兩個數(shù)字二進制按位對比柠衍,上下皆為1才是1,剩下都是0晶乔。
換言之任何數(shù)n&1.因為1前面位數(shù)都是0珍坊,只有最后一位才是1。所以得出的結果只能是1或者0.結果是1說明n的最后一位是1正罢,得出結果是0說明n的最后一位是0阵漏。
左移<<。這個二進制串往左移動,最高位舍去履怯,最低位補0回还。
右移>>和帶符號右移>>>。普通右移是往右移動叹洲,低位舍去柠硕,高位補0。
而這個帶符號右移是如果之前最高位是1运提,則低位舍去高位補1(保證移之前是負數(shù)移動后還是負數(shù))如果最高位是0則和普通右移一樣蝗柔。
這道題的解法只需要用到這三種基礎運算。
因為用上文用代碼邏輯來獲取一個數(shù)字的二進制只能保證整數(shù)的正確性民泵,所以這里要用位移來實現(xiàn)癣丧。
大概步驟就是一個一個獲取二進制的位數(shù),然后直接放到應該放的位置(這個是有個反轉的栈妆,比如獲取的最后一位要放到結果的第一位胁编,獲取的倒數(shù)第二位放到結果的第二位)。
說了這么多直接上代碼:

public class Solution {
    // you need treat n as an unsigned value
    public int reverseBits(int n) {
        //因為我第一版本用的while,所以不改了鳞尔,這里其實while或者for都可以嬉橙。
        int i = 0;
        int result = 0;
        //一共只有32位數(shù)。從0開始铅檩,所以到31就結束憎夷。
        while(i<32){
            //獲取二進制串的最后一位是0是1.
            int temp = n&1;
            //依次放置的是前置位的數(shù)字,因為i最大是31昧旨,所以放置的數(shù)位最后是0位拾给。
            result = result+(temp<<(31-i));
            //這個n每次右移一位,保證每次獲取的temp是不斷往上的位數(shù)兔沃。
            n = n>>1;
            i++;
        }
        return result;
    }
}

其實這道題感覺考得就是二進制的基礎運算知識蒋得。我覺得我最大的錯誤就是主觀解題,第一思路就不對(比如看了題目應該直接考慮用二進制解決問題乒疏,我還傻了吧唧的去字符串累加)额衙。
其實位移用的真不多,所以這里下意識反應不對我也原諒自己了怕吴,順便看了評論窍侧,說到了jdk中的二進制反轉,人家的代碼也只能嘆為觀止转绷,因為我死活也沒看懂伟件。看了半天加上找了好多資料议经,還問了人斧账,才勉強理解谴返。。


image.png

我隨便上個例子來講:


demo

上圖是我把jdk的方法源碼拆開來一步一步運行的咧织。其實乍一看規(guī)律挺不好找嗓袱。但是只要知道原理就能發(fā)現(xiàn)規(guī)律了。
其實第一步就是一個兩兩交換的步驟习绢。
從最后一位開始渠抹,最后一位和倒數(shù)第二位交換位置。倒數(shù)第三和倒數(shù)第四交換位置毯炮,倒數(shù)第五和倒數(shù)第六交換位置逼肯。。以此類推桃煎。如果到了第一個位數(shù)篮幢,前面沒有可交換的了則補0。圖上就是第一個1为迈,再前面沒數(shù)字了三椿,補了個0,01交換為10.然後結束了葫辐。
第二步是一個兩個一組兩兩交換搜锰。也是從最后兩位開始。倒數(shù)第二和倒數(shù)第一整體和倒數(shù)第四耿战,倒數(shù)第三這個整體交換蛋叼。倒數(shù)第八第七和倒數(shù)第六第五交換。剂陡。一直到首位還是沒有則補0狈涮。圖上因為到了第一二位,前面沒有了鸭栖,所以補了兩個0.也就是00 10兩組交換位置歌馍,變成了10 00。
第三步也是這樣晕鹊,但是變成了四個四個一組兩兩交換松却,沒有前面補0
第四步是以八為單位的交換,其實就是字符串翻轉了(因為二進制中的八位數(shù)是十六進制中的一個數(shù)字)溅话。
至此晓锻,整個翻轉完成。


群友提供的交換流程

然後這個題就到這了(做題用半小時飞几,研究jdk源碼半小天兒~~)

但是我還是要說一個問題:那就是性能問題带射。上個題做出來不難,但是對于jdk源碼理解起來很難循狰,而且還費時間窟社。但是最終一樣的代碼我在leetCode上運行了一下,jdk源碼方法和我自己寫的簡單位移計算的方法運行起來執(zhí)行速度相同绪钥,而且消耗也相同灿里?



其實這個就挺有意思了,思路上不是一種方法實現(xiàn)的程腹,但是性能是一樣的匣吊。之前因為看不懂源碼所以覺得應該就是厲害,但是現(xiàn)在有點墜下神壇的感覺寸潦。畢竟運行起來并沒有多優(yōu)秀吧色鸳?好吧,這是一個疑問见转,以后可能隨著知道的越多就能理解了命雀?下一題吧

位1的個數(shù)

編寫一個函數(shù),輸入是一個無符號整數(shù)斩箫,返回其二進制表達式中數(shù)字位數(shù)為 ‘1’ 的個數(shù)(也被稱為漢明重量)吏砂。

示例 1:
輸入:00000000000000000000000000001011
輸出:3
解釋:輸入的二進制串 00000000000000000000000000001011 中,共有三位為 '1'乘客。
示例 2:
輸入:00000000000000000000000010000000
輸出:1
解釋:輸入的二進制串 00000000000000000000000010000000 中狐血,共有一位為 '1'。
示例 3:
輸入:11111111111111111111111111111101
輸出:31
解釋:輸入的二進制串 11111111111111111111111111111101 中易核,共有 31 位為 '1'匈织。
提示:
請注意,在某些語言(如 Java)中牡直,沒有無符號整數(shù)類型缀匕。在這種情況下,輸入和輸出都將被指定為有符號整數(shù)類型井氢,并且不應影響您的實現(xiàn)弦追,因為無論整數(shù)是有符號的還是無符號的,其內部的二進制表示形式都是相同的花竞。
在 Java 中劲件,編譯器使用二進制補碼記法來表示有符號整數(shù)。因此约急,在上面的 示例 3 中零远,輸入表示有符號整數(shù) -3。
進階:
如果多次調用這個函數(shù)厌蔽,你將如何優(yōu)化你的算法牵辣?

思路:這道題就是上道題的變題,甚至比上題還要簡單奴饮,沒啥可說的了纬向。上題的思路上修改一下就行了择浊。

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int result = 0;
        for(int i=0;i<32;i++){
            int x = n&1;
            if(x==1){
                result++;
            }
            n = n>>1;
        }
        return result;
    }
}

一步步位移獲取每一位上的數(shù),如果是1則總數(shù)遞加逾条,不是就算了琢岩。簡單明了。這道題過师脂。

上升的溫度

image.png

又做到一個sql題担孔,但是因為這個題目讓我學會了一個函數(shù),所以在這列出來了吃警,
日期的相減:dateDiff(date1,date2)結果是日期1減去日期2 的天數(shù)糕篇;
完整答案:

select w.Id from Weather w,Weather w1 
where  DateDiff(w.RecordDate,w1.RecordDate)=1 
and w.Temperature> w1.Temperature

今天就整理到這里,如果稍微幫到你了記得點個喜歡點個關注喲~每天進步一點點酌心,也祝大家工作順順利利拌消!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谒府,隨后出現(xiàn)的幾起案子拼坎,更是在濱河造成了極大的恐慌,老刑警劉巖完疫,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泰鸡,死亡現(xiàn)場離奇詭異,居然都是意外死亡壳鹤,警方通過查閱死者的電腦和手機盛龄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芳誓,“玉大人余舶,你說我怎么就攤上這事∏绿剩” “怎么了匿值?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赂摆。 經常有香客問我挟憔,道長,這世上最難降的妖魔是什么烟号? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任绊谭,我火速辦了婚禮,結果婚禮上汪拥,老公的妹妹穿的比我還像新娘达传。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布宪赶。 她就那樣靜靜地躺著宗弯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逊朽。 梳的紋絲不亂的頭發(fā)上罕伯,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天,我揣著相機與錄音叽讳,去河邊找鬼。 笑死坟募,一個胖子當著我的面吹牛岛蚤,可吹牛的內容都是我干的。 我是一名探鬼主播懈糯,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼涤妒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了赚哗?” 一聲冷哼從身側響起她紫,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎屿储,沒想到半個月后贿讹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡够掠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年民褂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疯潭。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡赊堪,死狀恐怖,靈堂內的尸體忽然破棺而出竖哩,到底是詐尸還是另有隱情哭廉,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布相叁,位于F島的核電站遵绰,受9級特大地震影響,放射性物質發(fā)生泄漏钝荡。R本人自食惡果不足惜街立,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望埠通。 院中可真熱鬧赎离,春花似錦、人聲如沸端辱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荣病,卻和暖如春码撰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背个盆。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工脖岛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颊亮。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓柴梆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親终惑。 傳聞我的和親對象是個殘疾皇子绍在,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內容