Leetcode 08 java

283. 移動零

提示

給定一個數組?nums,編寫一個函數將所有?0?移動到數組的末尾,同時保持非零元素的相對順序。

請注意?,必須在不復制數組的情況下原地對數組進行操作。

示例 1:

輸入: nums = [0,1,0,3,12]
輸出: [1,3,12,0,0]

示例 2:

輸入: nums = [0]
輸出: [0]

提示:

  • 1 <= nums.length <= 104
  • -231?<= nums[i] <= 231?- 1

進階:你能盡量減少完成的操作次數嗎?

我的想法:

不能復制數組,又用哈希吧?或者用一個判斷?我不知道啊,快慢指針?根本不知道從哪里寫比較合適。

1 分析題目

這道題要求我們原地(in-place)將數組中的所有 0 移動到末尾,同時保持非零元素的相對順序。關鍵點有:

  1. 原地操作?- 不能創建新數組,必須直接修改原數組
  2. 保持非零元素順序?- 非零元素的相對位置不能改變
  3. 最小化操作次數?- 這是進階要求

你的思路分析

你的思考方向是正確的!提到了哈希表、判斷條件和快慢指針,這些都是解決數組問題的常用思路:

  • 哈希表:在這個問題中不需要,因為我們不需要記錄元素頻率或快速查找
  • 判斷條件:是的,我們需要判斷元素是否為 0
  • 快慢指針:這是解決這個問題的最佳方法!

2 正確解法:快慢指針法

快慢指針法是解決這類 "原地移動元素" 問題的經典方法。具體步驟:

  1. 快指針遍歷數組,尋找非零元素
  2. 慢指針記錄當前非零元素應該存放的位置
  3. 當快指針找到非零元素時,將其值賦給慢指針位置,然后慢指針前進一位
  4. 最后,將慢指針之后的所有位置填充為 0

java

運行

class Solution {public void moveZeroes(int[] nums) {int slow = 0;// 第一次遍歷,將所有非零元素移到前面for (int fast = 0; fast < nums.length; fast++) {if (nums[fast] != 0) {nums[slow] = nums[fast];slow++;}}// 第二次遍歷,將slow之后的所有位置填充為0for (int i = slow; i < nums.length; i++) {nums[i] = 0;}}
}

優化版本:減少操作次數

上面的方法需要兩次遍歷數組,實際上我們可以通過一次遍歷來完成:

優化版本(一次遍歷)

java

運行

class Solution {public void moveZeroes(int[] nums) {int slow = 0;for (int fast = 0; fast < nums.length; fast++) {if (nums[fast] != 0) {// 交換非零元素到前面int temp = nums[slow];nums[slow] = nums[fast];nums[fast] = temp;slow++;}}}
}

3 復雜度分析

  • 時間復雜度:O (n),只需要遍歷一次數組
  • 空間復雜度:O (1),只需要常數級額外空間

你的不足與改進建議

  1. 對快慢指針不夠熟悉:這是解決原地數組問題的重要技巧,建議多練習類似題目(如移除元素、刪除有序數組中的重復項等)
  2. 缺乏解題模板:對于數組問題,可以總結常見的解題思路:雙指針(快慢指針、左右指針)、滑動窗口、排序等
  3. 動手實踐不足:不要害怕開始寫代碼,先嘗試實現一個基本解法,再考慮優化

4 練習建議

推薦練習以下 LeetCode 題目來鞏固快慢指針技巧:

  1. 26. 刪除有序數組中的重復項
  2. 27. 移除元素
  3. 80. 刪除有序數組中的重復項 II

5 快慢指針和左右指針

官方給出的題解:

class Solution {public void moveZeroes(int[] nums) {int n = nums. length , left = 0 , right = 0 ;while (right < n ){if (nums[right] != 0 ){swap (nums, left ,right);left++;}right++;}}public void swap (int[]nums , int left ,int right){int temp =nums[left];nums[left] = nums [right];nums[right] = temp ;}
}

在這個 Java 實現中,left?和?right?指針本質上是快慢指針,而非傳統意義上的左右指針。這兩種指針模式的核心區別如下:

1. 快慢指針(Slow-Fast Pointers)

  • 同向移動:兩個指針從同一側(通常是左側)開始,以不同速度遍歷數組。
  • 核心邏輯
    • 慢指針:負責維護符合條件的元素位置(例如,非零元素的插入位置)。
    • 快指針:負責快速遍歷數組,尋找目標元素(例如,非零元素)。
  • 典型應用
    • 移除數組中的特定元素(如移除 0、重復項)。
    • 判斷鏈表是否有環(Floyd 判圈算法)。

你的代碼屬于快慢指針

  • left?指針:只有當?right?找到非零元素時才移動,相當于 “慢指針”。
  • right?指針:每次循環都移動,相當于 “快指針”。
  • 當?right?遇到非零元素時,通過交換將其移到?left?位置,然后?left?前進。

2. 左右指針(Left-Right Pointers)

  • 相向移動:兩個指針分別從數組的左右兩端開始,向中間移動。
  • 核心邏輯
    • 左指針:從左向右移動,尋找需要交換到右側的元素。
    • 右指針:從右向左移動,尋找需要交換到左側的元素。
  • 典型應用
    • 反轉數組(如反轉字符串)。
    • 兩數之和(有序數組)。
    • 快速排序中的分區操作。

左右指針的示例代碼(反轉數組):

java

運行

public void reverse(int[] nums) {int left = 0, right = nums.length - 1;while (left < right) {int temp = nums[left];nums[left] = nums[right];nums[right] = temp;left++;right--;}
}

3. 兩者對比總結

特征快慢指針左右指針
移動方向同向(左→右)相向(左←→右)
速度關系快指針移動更快指針交替移動或按需移動
核心目的篩選 / 移動特定元素反轉、查找配對元素
典型場景移除元素、鏈表操作反轉數組、兩數之和

4. 你的代碼分析

你的實現使用了快慢指針,因為:

  1. left?和?right?都從左側開始,同向移動。
  2. right?指針快速遍歷數組,left?指針僅在交換時前進。
  3. 通過交換非零元素到左側,保持了非零元素的相對順序。

如果使用左右指針(相向移動),則無法保證非零元素的順序(例如,可能將右側的非零元素交換到左側,打亂原有順序)。因此,本題必須使用快慢指針

總結

  • 快慢指針:同向移動,用于篩選 / 移動元素,保持順序
  • 左右指針:相向移動,用于反轉、配對或分區操作
  • 你的代碼是快慢指針的正確應用,適合本題需求

6?先做一下復制到新數組的方法

題友的啟發:

“沒有這么復雜,大家先做一下復制到新數組的方法(遍歷nums,所有非0數復制到新數組),然后再想一想,怎么優化到使用原數組做為新數組即可。”

明白了!我們可以先通過復制到新數組的方法直觀地解決問題,再逐步優化到原地操作。以下是完整的思路和代碼實現:

方法一:復制到新數組(直觀但不符合題目要求)

步驟

  1. 遍歷原數組?nums,將所有非零元素按順序放入新數組。
  2. 新數組剩余位置補 0。
  3. 將新數組復制回原數組(題目要求原地修改)。

代碼

java

運行

public void moveZeroes(int[] nums) {int n = nums.length;int[] temp = new int[n];int index = 0;// 復制非零元素到新數組for (int num : nums) {if (num != 0) {temp[index++] = num;}}// 剩余位置補0while (index < n) {temp[index++] = 0;}// 將新數組復制回原數組System.arraycopy(temp, 0, nums, 0, n);
}

復雜度

  • 時間:O (n)
  • 空間:O (n)(不符合題目要求)

方法二:原地操作(快慢指針優化)

優化思路
直接在原數組上操作,通過雙指針將非零元素移到左側,剩余位置補 0。

步驟

  1. 慢指針?left:記錄下一個非零元素應存放的位置。
  2. 快指針?right:遍歷數組,找到非零元素時與?left?交換,并右移?left
  3. 遍歷結束后,left?之后的位置全部補 0。

代碼

java

運行

public void moveZeroes(int[] nums) {int left = 0;// 將非零元素移到左側for (int right = 0; right < nums.length; right++) {if (nums[right] != 0) {nums[left] = nums[right];left++;}}// 剩余位置補0while (left < nums.length) {nums[left++] = 0;}
}

復雜度

  • 時間:O (n)
  • 空間:O (1)(符合題目要求)

方法三:進一步優化(一次遍歷 + 交換)

思路
在方法二的基礎上,通過交換操作避免第二次遍歷補 0。

步驟

  1. 雙指針?left?和?right?從左側開始。
  2. 當?right?遇到非零元素時,與?left?交換,并同時右移兩指針。
  3. 交換后,left?左側的元素均為非零,left?指向的位置為下一個可能需要交換的 0。

代碼

java

運行

public void moveZeroes(int[] nums) {int left = 0;for (int right = 0; right < nums.length; right++) {if (nums[right] != 0) {// 交換非零元素到左側int temp = nums[left];nums[left] = nums[right];nums[right] = temp;left++;}}
}

復雜度

  • 時間:O (n)(僅一次遍歷)
  • 空間:O (1)

對比與總結

方法時間復雜度空間復雜度操作次數
復制到新數組O(n)O(n)2n
原地操作(兩次遍歷)O(n)O(1)2n
原地操作(一次遍歷)O(n)O(1)n

結論
方法三最優,通過一次遍歷和交換操作,直接將所有非零元素移到左側,同時將 0 移到右側,操作次數最少。

7?System.arraycopy是什么?

System.arraycopy?是 Java 中用于數組復制的一個高效方法,位于?java.lang.System?類中。它允許你將一個數組的部分或全部元素復制到另一個數組中,是 Java 標準庫中性能最優的數組復制方式之一。

核心語法

java

運行

public static void arraycopy(Object src,      // 源數組int srcPos,      // 源數組的起始位置Object dest,     // 目標數組int destPos,     // 目標數組的起始位置int length       // 需要復制的元素個數
)

關鍵特性

  1. 高效性:基于 JVM 底層實現,通常比手動循環復制快得多。
  2. 靈活性:支持部分復制(指定起始位置和長度)。
  3. 兼容性
    • 僅適用于相同類型的數組(基本類型或引用類型)。
    • 對于引用類型,復制的是對象引用而非對象本身(淺拷貝)。
  4. 異常處理
    • 如果參數不合法(如數組類型不匹配、越界等),會拋出?ArrayStoreException?或?IndexOutOfBoundsException

示例代碼

1. 基本類型數組復制

java

運行

int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[5];// 將 src 中索引 1 開始的 3 個元素復制到 dest 的索引 2 位置
System.arraycopy(src, 1, dest, 2, 3);// 輸出 dest: [0, 0, 2, 3, 4]
2. 對象數組復制(淺拷貝)

java

運行

class Person {public int age;public Person(int age) { this.age = age; }
}Person[] src = {new Person(20), new Person(30)};
Person[] dest = new Person[2];System.arraycopy(src, 0, dest, 0, 2);// 修改 dest[0] 的屬性會影響 src[0]
dest[0].age = 25;
System.out.println(src[0].age); // 輸出: 25

與其他復制方式的對比

方法適用場景性能特點
System.arraycopy同類型數組高效復制最優底層實現,支持部分復制
Arrays.copyOf創建新數組并復制全部元素中等封裝了?arraycopy
Arrays.copyOfRange創建新數組并復制部分元素中等封裝了?arraycopy
for?循環自定義復雜復制邏輯最差靈活但效率低
clone()對象數組的淺拷貝中等僅適用于實現 Cloneable 的類

注意事項

  1. 避免數組重疊:如果源數組和目標數組是同一數組,且復制區域有重疊,可能導致意外結果。例如:

    java

    運行

    int[] arr = {1, 2, 3, 4, 5};
    // 錯誤示例:重疊復制可能導致數據覆蓋
    System.arraycopy(arr, 0, arr, 2, 3); // 結果可能不符合預期
    

  2. 多維數組:對于多維數組,arraycopy?只復制頂層數組的引用,不會遞歸復制子數組(仍為淺拷貝)。

常見應用場景

  • 數組擴容:創建更大的新數組并復制原內容。
  • 批量數據處理:高效轉移數組中的部分數據。
  • 集合實現:Java 中的?ArrayList?等集合類內部使用?arraycopy?實現動態擴容。

掌握?System.arraycopy?可以幫助你在 Java 中更高效地處理數組操作,尤其是在性能敏感的場景中。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93617.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93617.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93617.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LeetCode 56 - 合并區間

思路 排序&#xff1a;將所有區間按起始點從小到大排序。貪心合并&#xff1a;初始化一個結果列表&#xff0c;放入第一個區間。然后遍歷剩余區間&#xff0c;將當前區間與結果列表中的最后一個區間比較&#xff1a; 若重疊&#xff08;當前區間起點 ≤ 結果區間終點&#xff0…

DNS 正向查找與反向查找

DNS 區域是 DNS 中基本的組織單元&#xff0c;為域名定義了管理和權威邊界。一個 DNS 區域通常包含一系列 DNS 資源記錄&#xff0c;包括名稱到地址的映射&#xff08;正向查找&#xff09;和地址到名稱的映射&#xff08;反向查找&#xff09;。這些區域對于高效管理和解析網絡…

Oracle ERP FORM開發 — 新增查詢條件

1 根據值來查詢具體流程步驟看第2節&#xff0c;這里提供核心的增加查詢條件的觸發器代碼&#xff1a;1.1 可完全匹配的值比如“是”&#xff0c;“否”&#xff0c;“物料”&#xff0c;“”汽車 等等這些可以直接通過對應的值匹配&#xff0c;特點就是詞語&#xff0c;短小。…

Flutter實現列表功能

在Flutter中,可以通過ListView和ListTile等組件來實現類似Android中RecyclerView和Adapter的功能。以下是一個通用的設計架構,用于設計列表數據: 1. 定義數據模型 首先,定義一個數據模型類,用于存儲列表中每一項的數據。例如: class ItemModel {final String title;fi…

2.1、Redis的單線程本質和多線程的操作

Redis的單線程本質 1. 核心單線程部分 #mermaid-svg-iFErSltthPIEsuiP {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-iFErSltthPIEsuiP .error-icon{fill:#552222;}#mermaid-svg-iFErSltthPIEsuiP .error-text{fil…

文件權限值的表示方法

文章目錄字符表示方法8 進制數值表示方法字符表示方法 Linux表示說明Linux表示說明r--只讀-w-僅可寫--x僅可執行rw-可讀可寫-wx可寫可執行r-x可讀可執行rwx可讀可寫可執行---無權限 8 進制數值表示方法 權限符號8進制2進制r4100w2010x1001rw6110rx5101wx3011rwx7111---0000

【38】WinForm入門到精通 ——WinForm平臺為AnyCPU 無法切換為x64,也無法添加 x64及其他平臺

WinForm 是 Windows Form 的簡稱&#xff0c;是基于 .NET Framework 平臺的客戶端&#xff08;PC軟件&#xff09;開發技術&#xff0c;是 C# 語言中的一個重要應用。.NET 提供了大量 Windows 風格的控件和事件&#xff0c;可以直接拿來使用。本專欄內容是按照標題序號逐漸深入…

門控激活函數:GLU/GTU/Swish/HSwish/Mish/SwiGLU

10 門控激活函數 10.1 GLU&#xff1a;門控線性單元函數Gated Linear Unit10.2 GTU&#xff1a;門控Tanh單元函數Gated Tanh Unit自門控激活函數&#xff08;Self-gated activation function&#xff09;是一種通過自身機制動態調節信息流動的激活函數&#xff0c;其核心在于模…

Linux內核IPv4多播路由深度解析:從數據結構到高效轉發

多播路由是網絡通信的核心技術之一&#xff0c;Linux內核通過精密的多層設計實現了高性能的IPv4多播路由功能。本文將深入剖析其核心實現機制&#xff0c;揭示其高效運轉的秘密。一、核心數據結構&#xff1a;路由系統的基石1. 多播路由表&#xff08;struct mr_table&#xff…

ffmpeg-7.1.1 下載安裝 windows 版,MP4 轉 m3u8 切片,遇到報錯 Unrecognized option ‘vbsf‘的解決辦法

工作中偶爾會需要造指定大小的文檔文件&#xff0c;不要求內容&#xff0c;可以隨意填充任意無毒內容&#xff0c;所以打算用ts文件填充&#xff0c;現記錄下過程。一、下載 ffmpeg廢話不多說&#xff0c;上鏈接&#xff0c;https://ffmpeg.org/會跳轉新頁面&#xff0c;向下拉…

Linux網絡編程:網絡基礎概念(下)

目錄 前言&#xff1a; 一、網絡傳輸基本流程 1.1、認識MAC地址 1.2、認識IP地址 二、socket編程預備 2.1、端口號 2.2、傳輸層的代表 2.3、網絡字節序 2.4、sockaddr 結構 總結&#xff1a; 前言&#xff1a; 大家好&#xff0c;上一篇文章&#xff0c;我們說到了…

亞馬遜廣告進階指南:如何優化流量實現新品快速起量

“新品上架如何快速獲取精準流量&#xff1f;”“如何降低ACOS同時提升轉化率&#xff1f;”“競品流量攔截有哪些高效方法&#xff1f;”“關鍵詞廣告和ASIN廣告如何協同投放&#xff1f;”“人工投放效果不穩定&#xff0c;AI工具真的能解決問題嗎&#xff1f;”如果你也在思…

路徑平滑優化算法--Clothoid 路徑平滑

路徑平滑優化算法–Clothoid 路徑平滑 文章目錄路徑平滑優化算法--Clothoid 路徑平滑0 為什么選 Clothoid&#xff1f;1 數學基礎&#xff1a;嚴謹推導&#xff08;Mathematical Foundation&#xff09;可視化解釋1.1 曲率線性假設1.2 切向角&#xff08;Heading Angle&#…

PCB學習筆記(一)

文章目錄一、PCB的制作過程了解1.1 覆銅板一、核心作用&#xff1a;制作印制電路板&#xff08;PCB&#xff09;二、不同類型覆銅板的針對性用途三、延伸應用1.2 覆銅板和信號線的關系一、覆銅板不是“全是銅”&#xff0c;原始結構是“絕緣基材銅箔”二、信號線就是銅&#xf…

【19】C# 窗體應用WinForm ——【列表框ListBox、復選列表框CheckedListBox】屬性、方法、實例應用

文章目錄9 復選列表框CheckedListBox10. 列表框ListBox10.1 實例&#xff1a;買菜10.2 實例&#xff1a;購菜 應用二WinForm 是 Windows Form 的簡稱&#xff0c;是基于 .NET Framework 平臺的客戶端&#xff08;PC軟件&#xff09;開發技術&#xff0c;是 C# 語言中的一個重要…

新注冊企業信息查詢“數據大集網”:驅動企業增長的源頭活水

商貿繁榮的齊魯大地上&#xff0c;“趕大集”曾是千年傳承的民間智慧。而今天&#xff0c;一場以新注冊企業信息為核心的“數據大集”正悄然重塑商業生態——數據大集網&#xff0c;以“聚天下好數&#xff0c;促萬企互聯”為使命&#xff0c;將分散的企業信息轉化為精準商機&a…

項目目標如何拆解,才能提高執行效率和效果

項目目標要有效拆解以提高執行效率和效果&#xff0c;需要遵循以下關鍵步驟&#xff1a;明確整體目標、劃分階段性目標和里程碑、具體化任務細分并設定優先級、分配明確的責任人和時間節點、持續跟蹤與反饋調整。其中&#xff0c;劃分階段性目標和里程碑尤為重要&#xff0c;通…

spring-ai-alibaba動態 Prompt 最佳實踐

Spring AI Alibaba 使用 Nacos 的配置中心能力來動態管理 AI 應用的 Prompt。以此來實現動態更新 Prompt 的功能。 環境準備 Nacos: 具備配置中心能力的 Nacos&#xff0c;本例中使用 Nacos 3.0.2。Nacos 2.X 亦可&#xff0c; spring-ai版本1.0.0 &#xff0c;spring-ai-al…

基于詞頻統計、關鍵詞提取、情感分析與AI大模型自動生成系統的設計與實現

文章目錄有需要本項目的代碼或文檔以及全部資源&#xff0c;或者部署調試可以私信博主一、研究背景與項目意義二、項目目標與研究內容三、系統架構與功能模塊1. AI對話生成模塊2. 分詞與關鍵詞提取模塊3. 情感分析模塊4. 行為預測模塊5. 系統管理模塊6. 可視化展示模塊四、技術…

JVM工具

首先&#xff0c;JDK 自帶了很多監控工具&#xff0c;都位于 JDK 的 bin 目錄下&#xff0c;其中最常用的是 jconsole 和 jvisualvm 這兩款視圖監控工具。 一、jps(Java Process Status) 用于查看有權訪問的虛擬機的進程&#xff0c;并顯示他們的進程號 -v&#xff1a;列出虛擬…