LeetCode算 法 實 戰 - - - 雙 指 針 與 移 除 元 素、快 慢 指 針 與 刪 除 有 序 數 組 中 的 重 復 項
- 第 一 題 - - - 移 除 元 素
- 方 法 一 - - - 雙 重 循 環
- 方 法 二 - - - 雙 指 針
- 方 法 三 - - - 相 向 雙 指 針(面 對 面 移 動)
- 第 二 題 - - - 刪 除 有 序 數 組 中 的 重 復 項
- 方 法 一 - - - 快 慢 指 針
- 方 法 二 - - - 改 進 方 法 一
- 總 結
💻作 者 簡 介:曾 與 你 一 樣 迷 茫,現 以 經 驗 助 你 入 門 數據 結 構。
💡個 人 主 頁:@笑口常開xpr 的 個 人 主 頁
📚系 列 專 欄:硬 核 數 據 結 構 與 算 法
?代 碼 趣 語:恰 當 的 數 據 視 圖 實 際 上 就 決 定 了 程 序 的 結 構。
💪代 碼 千 行,始 于 堅 持,每 日 敲 碼,進 階 編 程 之 路。
📦gitee 鏈 接:gitee
?????????在 數 據 結 構 的 世 界 里,每 一 種 設 計 都 可 能 孕 育 出 驚 人 的 效 率 變 革。你 是 否 深 思 過,一 組 精 心 組 織 的 數 據 究 竟 能 創 造 怎 樣 的 奇 跡?每 一 次 挖 掘 底 層 原 理,都 是 與 計 算 機 智 慧 的 巔 峰 對 話;每 一 次 剖 析 存 儲 模 式,都 在 破 解 數 據 世 界 的 終 極 密 碼。準 備 好 迎 接 這 場 盛 宴 了 嗎?讓 我 們 一 同 探 尋 雙 指 針 的 無 盡 奧 秘,見 證 它 如 何 重 塑 數 字 時 代 的 運 行 法 則!
第 一 題 - - - 移 除 元 素
移 除 元 素
描 述:給 你 一 個 數 組 nums 和 一 個 值 val,你 需 要 原 地 移 除 所 有 數 值 等 于 val 的 元 素。元 素 的 順 序 可 能 發 生 改 變。然 后 返 回 nums 中 與 val 不 同 的 元 素 的 數 量。
?????????假 設 nums 中 不 等 于 val 的 元 素 數 量 為 k,要 通 過 此 題,您 需 要 執 行 以 下 操 作:
1、更 改 nums 數 組,使 nums 的 前 k 個 元 素 包 含 不 等 于 val 的 元 素。nums 的 其 余 元 素 和 nums 的 大 小 并 不 重 要。
2、返 回 k。
示 例 1:
輸 入:nums = [3,2,2,3], val = 3
輸 出:2, nums = [2,2,,]
解 釋:你 的 函 數 函 數 應 該 返 回 k = 2, 并 且 nums 中 的 前 兩 個 元 素 均 為 2。
示 例 2:
輸 入:nums = [0,1,2,2,3,0,4,2], val = 2
輸 出:5, nums = [0,1,4,0,3,,,_]
解 釋:你 的 函 數 應 該 返 回 k = 5,并 且 nums 中 的 前 五 個 元 素 為 0,0,1,3,4。
注 意 這 五 個 元 素 可 以 任 意 順 序 返 回。
提 示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
方 法 一 - - - 雙 重 循 環
思 路 分 析
?????????題 目 要 求 去 除 一 組 數 中 的 重 復 元 素,并 且 題 目 強 調 nums 的 其 余 元 素 和 nums 的 大 小 并 不 重 要。
?????????可 以 通 過 雙 重 循 環 遍 歷 數 組,當 發 現 目 標 值 時,將 其 后 的 所 有 元 素 依 次 前 移 一 位,從 而 覆 蓋 掉 目 標 值 元 素。(類 似 順 序 表 中 的 頭 插)這 樣 做 的 目 的 是 在 原 數 組 上 直 接 進 行 元 素 移 除 操 作,不 使 用 額 外 的 存 儲 空 間,所 以 空 間 復 雜 度 是 O(1),時 間 復 雜 度 是 O(N^2)。
溫 馨 提 示:讀 者 們 ,先 自 己 寫 代 碼,這 是 提 升 編 程 能 力 的 好 機 會。若 未 達 要 求 ,別 氣 餒 ,參 考 下 文 解 釋 會 有 新 收 獲。
下 面 展 示
代 碼 示 例
。
int removeElement(int* nums, int numsSize, int val)
{int i = 0;int count = 0;int temp1 = numsSize;while (i < temp1) {if (nums[i] == val) {int temp = i;while (temp < numsSize - 1) {nums[temp] = nums[temp + 1];temp++;}count++;temp1--;}else {i++;}}return numsSize - count;
}
注 意
?????????網 站 上 刷 題 分 為 IO 型 和 接 口 型,IO 型 是 使 用 scanf 得 到 輸 入,結 果 使 用 printf,并 且 要 寫 出 完 整 程 序。接 口 型 是 結 果 通 過 返 回 值 返 回,只 寫 實 現 的 函 數,是 一 部 分 程 序。在 LeetCode 上 刷 題 幾 乎 都 是 接 口 型,調 試 起 來 比 較 麻 煩,牛 客 網 中 有 IO 型 和 接 口 型。
以 數 組 元 素 為 3,2,2,3,1,2,4,2 val 為 2 進 行 動 畫 演 示。
方 法 二 - - - 雙 指 針
雙 指 針
?????????雙 指 針 是 一 種 常 用 的 算 法 技 巧,利 用 它 們 之 間 的 相 對 移 動 來 高 效 地 解 決 問 題,核 心 思 想 是 將 遍 歷 數 組 和 原 地 修 改 分 離,通 過 一 次 遍 歷 完 成 元 素 篩 選 和 數 組 重 構,通 過 指 針 的 位 置 關 系 快 速 定 位 目 標 元 素 或 區 間。時 間 復 雜 度 優 化 至 O(n)。
同 向 雙 指 針(快 慢 雙 指 針)
?????????同 向 即 同 一 方 向,兩 個 指 針 朝 同 一 方 向 移 動,速 度 可 能 不 同(快 指 針 步 長 更 大,慢 指 針 步 長 較 小)。
思 路 分 析
?????????題 目 要 求 刪 除 數 組 中 等 于 val 的 元 素,因 此 輸 出 數 組 的 長 度 一 定 小 于 等 于 輸 入 數 組 的 長 度,我 們 可 以 把 輸 出 的 數 組 直 接 寫 在 輸 入 數 組 上。使 用 快 指 針 right 指 向 當 前 將 要 處 理 的 元 素,慢 指 針 left 指 向 下 一 個 將 要 賦 值 的 位 置。
?????????如 果 快 指 針 指 向 的 元 素 不 等 于 val,它 一 定 是 輸 出 數 組 的 一 個 元 素,我 們 就 將 快 指 針 指 向 的 元 素 復 制 到 慢 指 針 位 置,然 后 將 快 慢 指 針 同 時 右 移;
?????????如 果 快 指 針 指 向 的 元 素 等 于 val,它 不 能 在 輸 出 數 組 里,此 時 左 指 針 不 動 ,右 指 針 右 移 一 位。
這 里 以 示 例 1 為 例 進 行 演 示:
溫 馨 提 示:讀 者 們 ,先 自 己 寫 代 碼,這 是 提 升 編 程 能 力 的 好 機 會。若 未 達 要 求 ,別 氣 餒 ,參 考 下 文 解 釋 會 有 新 收 獲。
下 面 展 示
代 碼 示 例
。
int removeElement(int* nums, int numsSize, int val)
{int left = 0;for (int right = 0; right < numsSize; right++) {if (nums[right] != val) {nums[left] = nums[right];left++;}}return left;
}
時 間 復 雜 度:O(N)
空 間 復 雜 度:O(1)
方 法 三 - - - 相 向 雙 指 針(面 對 面 移 動)
特 點
?????????兩 個 指 針 分 別 從 數 組 的 兩 端 出 發,相 向 移 動。
思 路 分 析
?????????設 左 指 針 位 于 數 組 的 首 位,右 指 針 位 于 數 組 的 末 尾,向 中 間 移 動 遍 歷 該 序 列。如 果 左 指 針 left 指 向 的 元 素 等 于 val,此 時 將 右 指 針 right 指 向 的 元 素 復 制 到 左 指 針 left 的 位 置,然 后 右 指 針 right 左 移 一 位。如 果 賦 值 過 來 的 元 素 恰 好 也 等 于 val,繼 續 把 右 指 針 right 指 向 的 元 素 的 值 賦 值 過 來(左 指 針 left 指 向 的 等 于 val 的 元 素 的 位 置 繼 續 被 覆 蓋),直 到 左 指 針 指 向 的 元 素 的 值 不 等 于 val 為 止。當 左 指 針 left 和 右 指 針 right 重 合 的 時 候,左 右 指 針 遍 歷 完 數 組 中 所 有 的 元 素。
這 里 以 示 例 1 為 例 進 行 演 示:
溫 馨 提 示:讀 者 們 ,先 自 己 寫 代 碼,這 是 提 升 編 程 能 力 的 好 機 會。若 未 達 要 求 ,別 氣 餒 ,參 考 下 文 解 釋 會 有 新 收 獲。
下 面 展 示
代 碼 示 例
。
int removeElement(int* nums, int numsSize, int val)
{int left = 0, right = numsSize;while (left < right) {if (nums[left] == val) {nums[left] = nums[right - 1];right--;} else {left++;}}return left;
}
時 間 復 雜 度:O(N)
空 間 復 雜 度:O(1)
第 二 題 - - - 刪 除 有 序 數 組 中 的 重 復 項
描 述:給 你 一個 非 嚴 格 遞 增 排 列 的 數 組 nums,請 你 原 地 刪 除 重 復 出 現 的 元 素,使 每 個 元 素 只 出 現 一 次 ,返 回 刪 除 后 數 組 的 新 長 度。元 素 的 相 對 順 序 應 該 保 持 一 致。然 后 返 回 nums 中 唯 一 元 素 的 個 數。
?????????考 慮 nums 的 唯 一 元 素 的 數 量 為 k,更 改 數 組 nums,使 nums 的 前 k 個 元 素 包 含 唯 一 元 素,并 按 照 它 們 最 初 在 nums 中 出 現 的 順 序 排 列。nums 的 其 余 元 素 與 nums 的 大 小 不 重 要。返 回 k。
示 例 1:
輸 入:nums = [1,1,2]
輸 出:2, nums = [1,2,_]
解 釋:函 數 應 該 返 回 新 的 長 度 2 ,并 且 原 數 組 nums 的 前 兩 個 元 素 被 修 改 為 1, 2 。不 需 要 考 慮 數 組 中 超 出 新 長 度 后 面 的 元 素。
示 例 2:
輸 入:nums = [0,0,1,1,1,2,2,3,3,4]
輸 出:5, nums = [0,1,2,3,4]
解 釋:函 數 應 該 返 回 新 的 長 度 5 , 并 且 原 數 組 nums 的 前 五 個 元 素 被 修 改 為 0, 1, 2, 3, 4。不 需 要 考 慮 數 組 中 超 出 新 長 度 后 面 的 元 素。
方 法 一 - - - 快 慢 指 針
思 路 分 析:題 目 要 求 去 除 數 組 中 的 重 復 元 素,并 且 題 目 強 調 使 數 組 的 前 k 個 元 素 包 含 唯 一 元 素,并 按 照 它 們 最 初 在 nums 中 出 現 的 順 序 排 列。可 以 使 用 雙 指 針 的 快 慢 指 針 求 解 這 個 問 題,慢 指 針 指 向 當 前 唯 一 元 素,快 指 針 遍 歷 數 組,發 現 不 同 元 素 時 更 新 慢 指 針。
溫 馨 提 示:讀 者 們 ,先 自 己 寫 代 碼,這 是 提 升 編 程 能 力 的 好 機 會。若 未 達 要 求 ,別 氣 餒 ,參 考 下 文 解 釋 會 有 新 收 獲。
下 面 展 示
代 碼 示 例
。
int removeDuplicates(int* nums, int numsSize)
{int left = 0; //慢指針:指向當前無重復數組的最后位置int right = left + 1; //快指針:從第二個元素開始遍歷while(right < numsSize) //遍歷整個數組{if(nums[left] == nums[right]){right++; //遇到重復元素,右移快指針}else{left++; //找到新元素,更新慢指針位置nums[left] = nums[right]; //將新元素復制到慢指針位置}}return left + 1; //返回無重復數組的長度(加上第1個元素)
}
空 間 復 雜 度 是 O(1)
時 間 復 雜 度 是 O(N)
方 法 二 - - - 改 進 方 法 一
?????????相 較 于 方 法 1,方 法 2 對 方 法 1 進 行 了 改 進,改 進 如 下:
邊 界 條 件 處 理:
增 加 了 numsSize == 0 的 判 斷,避 免 空 數 組 導 致 的 未 定 義 行 為。
指 針 初 始 化 優 化:
left 和 right 均 初 始 化 為 1,因 為 第 一 個 元 素(索 引 0)天 然 無 需 處 理。
比 較 邏 輯 簡 化:
通 過 比 較 nums[right-1] 和 nums[right],直 接 判 斷 當 前 元 素 是 否 與 前 一 個 重 復。當 發 現 不 重 復 元 素 時,直 接 賦 值 到 left 位 置,并 遞 增 left。
溫 馨 提 示:讀 者 們 ,先 自 己 寫 代 碼,這 是 提 升 編 程 能 力 的 好 機 會。若 未 達 要 求 ,別 氣 餒 ,參 考 下 文 解 釋 會 有 新 收 獲。
下 面 展 示
代 碼 示 例
。
int removeDuplicates(int* nums, int numsSize)
{if(numsSize == 0) //處理空數組的邊界情況{return 0;}int left = 1; //慢指針:從第二個位置開始寫入int right = 1; //快指針:從第二個位置開始遍歷while(right < numsSize) //遍歷整個數組{if(nums[right-1] != nums[right]) //當前元素與前一個不同{nums[left] = nums[right]; //將新元素寫入left位置left++; //移動left指針}right++; //無論如何都移動right指針}return left; //返回無重復數組的長度
}
時 間 復 雜 度 是 O(N)
空 間 復 雜 度 是 O(1)
總 結
?????????至 此,關 于 雙 指 針 的 探 索 暫 告 一 段 落,但 你 的 編 程 征 程 才 剛 剛 啟 航。編 寫 代 碼 是 與 計 算 機 邏 輯 深 度 對 話,過 程 中 雖 會 在 結 構 設 計、算 法 實 現 的 困 境 里 掙 扎,但 這 些 磨 礪 加 深 了 對 代 碼 邏 輯 和 數 據 組 織 的 理 解。愿 你 合 上 電 腦 后,靈 感 不 斷,在 數 據 結 構 的 世 界 里 持 續 深 耕,書 寫 屬 于 自 己 的 編 程 傳 奇,下 一 次 開 啟,定 有 全 新 的 精 彩 等 待。小 編 期 待 重 逢,盼 下 次 閱 讀 時 見 證 你 們 更 大 的 進 步,共 赴 代 碼 之 約!