一文秒殺三個經典面試求和問題
一文帶你秒殺三個求和問題
今天為大家帶來三道求和問題,通過文字,圖畫,動圖為大家解析,很容易就能讀懂,每道題目都是經典題,大家快來打卡吧。
題目來源:leetcode 1.兩數之和(簡單) 15.三數之和(中等) 18.四數之和(中等)
兩數之和
題目描述:
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和為目標值的那 兩個 整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,數組中同一個元素不能使用兩遍。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
題目很容易理解,即讓查看數組中有沒有兩個數的和為目標數,如果有的話則返回兩數下標,我們為大家提供兩種解法雙指針(暴力)法,和哈希表法
哈希表
解析
哈希表的做法很容易理解,我們只需通過一次循環即可,假如我們的 target 值為 9,當前指針指向的值為 2 ,我們只需從哈希表中查找是否含有 7,因為9 – 2 =7 。如果含有 7 我們直接返回即可,如果不含有則將當前的2存入哈希表中,指針移動,指向下一元素。註: key 為元素值,value 為元素索引。
動圖解析:
是不是很容易理解,下面我們來看一下題目程式碼。
題目程式碼:
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0; i < nums.length; i++){
//如果存在則返回
if(map.containsKey(target-nums[i])){
return new int[]{map.get(target-nums[i]),i};
}
//不存在則存入
map.put(nums[i],i);
}
return new int[0];
}
}
雙指針(暴力)法
解析
雙指針(L,R)法的思路很簡單,L指針用來指向第一個值,R指針用來從第L指針的後面查找數組中是否含有和L指針指向值和為目標值的數。見下圖
例:綠指針指向的值為3,藍指針需要在綠指針的後面遍歷查找是否含有 target – 3 = 2的元素,若含有返回即可。
題目程式碼
class Solution {
public int[] twoSum(int[] nums, int target) {
if(nums.length < 2){
return new int[0];
}
int[] rearr = new int[2];
//查詢元素
for(int i = 0; i < nums.length; i++){
for(int j = i+1; j < nums.length; j++ ){
if(nums[i] + nums[j] ==target){
rearr[0] = i;
rearr[1] = j;
}
}
}
return rearr;
}
}
三數之和
題目描述:
給你一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
示例:
給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合為:
[
[-1, 0, 1],
[-1, -1, 2]
]
這個題目算是對剛才題目的升級,剛才題目我們是只需返回一個例子即可,但是這個題目是讓我們返回所有情況,這個題目我們需要返回三個數相加為 0 的所有情況,但是我們需要去掉重複的三元組(算是該題的核心),所以這個題目還是挺有趣的,大家記得打卡呀。
哈希表:
解析
我們這個題目的哈希表解法是很容易理解的,我們首先將數組排序,排序之後我們將排序過的元素存入哈希表中,我們首先通過兩層遍歷,確定好前兩位數字,那麼我們只需要哈希表是否存在符合情況的第三位數字即可,跟暴力解法的思路類似,很容易理解,但是這裡我們需要注意的情況就是,例如我們的例子為[-2 , 1 , 1],如果我們完全按照以上思路來的話,則會出現兩個解,[-2 , 1 , 1]和[1 , 1, -2]。具體原因,確定 -2,1之後發現 1 在哈希表中,存入。確定 1 ,1 之後發現 -2 在哈希表中,存入。所以我們需要加入一個約束避免這種情況,那就是我們第三個數的索引大於第二個數時才存入。
上面這種情況時是不可以存入的,因為我們雖然在哈希表中找到了符合要求的值,但是 -2 的索引為 0 小於 2 所以不可以存入。
題目程式碼
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums.length < 3){
return new ArrayList<>();
}
//排序
Arrays.sort(nums);
HashMap<Integer,Integer> map = new HashMap<>();
List<List<Integer>> resultarr = new ArrayList<>();
//存入哈希表
for(int i = 0; i < nums.length; i++){
map.put(nums[i],i);
}
Integer t;
int target = 0;
for(int i = 0; i < nums.length; ++i){
target = -nums[i];
//去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
for(int j = i + 1; j < nums.length; ++j){
if(j > i+1 && nums[j] == nums[j-1]){
continue;
}
if((t = map.get(target - nums[j])) != null){
//符合要求的情況,存入
if(t > j){
resultarr.add(new ArrayList<>
(Arrays.asList(nums[i], nums[j], nums[t])));
}
else{
break;
}
}
}
}
return resultarr;
}
}
多指針
解析:
如果我們將上個題目得指針解法稱做是雙指針的話,那麼這個題目用到的方法就是三指針,因為我們是三數之和嘛,一個指針對應一個數,下面我們看一下具體思路,其實原理很簡單,我們先將數組排序,直接 Arrays.sort() 解決,排序之後處理起來就很容易了。下面我們來看下三個指針的初始位置。
初始情況見上圖,我們看當前情況,三數之和為 -3 ,很顯然不是 0 ,那麼我們應該怎麼做呢?
我們設想一下,我們當前的三數之和為 -3 < 0 那麼我們如果移動橙色指針的話則會讓我們的三數之和變的更小,因為我們的數組是有序的,所以我們移動橙色指針(藍色不動)時和會變小,如果移動藍色指針(橙色不動)的話,三數之和則會變大,所以這種情況則需要向右移動我們的藍色指針,找到三數之和等於 0 的情況進行保存,如果三數之和大於 0 的話,則需要移動橙色指針,途中有三數之和為 0 的情況則保存。直至藍橙兩指針相遇跳出該次循環,然後我們的綠指針右移一步,繼續執行上訴步驟。但是這裡我們需要注意的一個細節就是,我們需要去除相同三元組的情況,我們看下面的例子。
這裡我們發現 0 – 1 + 1 = 0,當前情況是符合的,所以我們需要存入該三元組,存入後,藍色指針向後移動一步,橙色指針向前移動一步,我們發現仍為 0 -1 + 1 = 0 仍然符合,但是如果繼續存入該三元組的話則不符合題意,所以我們需要去重。這裡可以藉助HashSet但是效率太差,不推薦。這裡我們可以使用 while 循環將藍色指針移動到不和剛才相同的位置,也就是直接移動到元素 0 上,橙色指針同樣也是。則是下面這種情況,這樣我們就實現了去重,然後繼續判斷當前三數之和是否為 0 。
動圖解析:
題目程式碼:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> arr = new ArrayList<List<Integer>>();
if(nums.length < 3){
return arr;
}
//排序
Arrays.sort(nums);
if(nums[0] > 0){
return arr;
}
for(int i = 0; i < nums.length-2; i++){
int target = 0 - nums[i];
//去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
int l = i+1;
int r = nums.length - 1;
while(l < r){
if(nums[l] + nums[r] == target){
//存入符合要求的值
arr.add(new ArrayList<>(Arrays.asList(nums[i], nums[l], nums[r])));
//這裡需要注意順序
while(l < r && nums[l] == nums[l+1]) l++;
while(l < r && nums[r] == nums[r-1]) r--;
l++;
r--;
}
else if(nums[l] + nums[r] > target){
r--;
}
else{
l++;
}
}
}
return arr;
}
}
四數之和
題目描述
給定一個包含 n 個整數的數組 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等?找出所有滿足條件且不重複的四元組。
注意:
答案中不可以包含重複的四元組。
示例:
給定數組 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
滿足要求的四元組集合為:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
我們已經完成了兩數之和和三數之和,這個題目應該就手到擒來了,因為我們已經知道這類題目的解題框架,兩數之和呢,我們就先固定第一個數 ,然後移動指針去找第二個符合的,三數之和,固定一個數,雙指針去找符合情況的其他兩位數,那麼我們四數之和,也可以先固定兩個數,然後利用雙指針去找另外兩位數。所以我們來搞定他吧。
多指針:
解析:
三數之和是,我們首先確定一個數,然後利用雙指針去找另外的兩個數,我們在這個題目裡面的解題思路是需要首先確定兩個數然後利用雙指針去找另外兩個數,和三數之和思路基本一致很容易理解。我們具體思路可以參考下圖。
這裡需要注意的是,我們的 target 不再和三數之和一樣為 0 ,target 是不固定的,所以解題思路不可以完全照搬上面的題目。另外這裡也需要考慮去重的情況,思路和上題一致。
上圖則為我們查找到一個符合條件的四元組的情況,查找成功之後,下一步移動藍色指針,重新定義綠藍指針,繼續執行上面步驟。
動圖解析:
題目程式碼:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
if(nums.length < 4){
return new ArrayList<>();
}
Arrays.sort(nums);
List<List<Integer>> arr = new ArrayList<>();
for(int i = 0; i < nums.length-3; ++i){
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
for(int j = i+1; j < nums.length-2; j++){
if(j > i+1 && nums[j] == nums[j-1]){
continue;
}
int l = j+1;
int r = nums.length-1;
while(l < r){
int sum = nums[i] + nums[j] + nums[l] + nums[r];
if(sum == target){
//存入
arr.add(new ArrayList<>
(Arrays.asList(nums[i], nums[j], nums[l], nums[r])));
//去重
while(l < r && nums[l] == nums[l+1]){
l++;
}
while(l < r && nums[r] == nums[r-1]){
r--;
}
l++;
r--;
}
else if(sum > target){
r--;
}
else{
l++;
}
}
}
}
return arr;
}
}
通過上面的三個例子,大家是不是把此類求和問題摸的透透的啦,如果能感覺到這個文章寫的很用心的話,能給您帶來一丟丟幫助的話,能麻煩您給這個文章點個贊嗎?這樣我就巨有動力寫下去啦。
另外大家如果需要其他精選演算法題的動圖解析,大家可以微信關注下袁廚的演算法小屋,我是袁廚一個酷愛做飯所以自己考取了廚師證的菜雞程式設計師,會一直用心寫下去的,感謝支援!