數據結構題目①——數組

前言

本篇文章為博主進行代碼隨想錄——數組練習后的總結會涉及到每一道題目的詳細的思路整理,以及本人的易錯點,希望對大家有所幫助

數組介紹:

數組在C語言中就已經有所涉及,它是一個最基礎的數據結構,而在數據結構中,通常它屬于線性表中的順序表(Sequence List),它是一種特殊的線性存儲結構。

特點:

邏輯上相鄰的數據元素,在物理次序上也是相鄰的。——也就是說它們挨著存儲

任意元素都可在相同的時間內存取,即順序存儲的數組是一個隨即存取結構

順序存儲:行優先和列優先兩種順序

行優先——每一行的第一個元素位于低地址,最后的位于高地址,且連續

列優先——每一行的第一個元素位于低地址,最后的位于高地址,且連續

優點:

它訪問速度快,但是無法高效插入和刪除元素

數組理論基礎

數組是存放在連續內存空間上的相同類型數據的集合

數組的元素是不能刪的,只能覆蓋

如果使用C++的話,要注意vector 和 array的區別,vector的底層實現是array,嚴格來講vector是容器,不是數組。

對于二維數組來說——它的順序是否連續呢——語言不同,會造成一定的差異,而c++中的二維數組是連續的,Java卻不是這樣存儲的。

?二分查找

題目:. - 力扣(LeetCode)

方法——使用二分查找的方式

注意——

1.要看清題目是否給出有序數組,這是一個二分查找的條件

2.循環不變量的思想——區間設置到底是左閉右開還是左閉右閉

第一次寫可能出現的問題——

1.if中的==而不是=

2.left和right是數組的位置標號,而非數組內容

3.要注意從開始就考慮是選取左閉右閉還是左閉右開

4.注意函數最后要在while循環外有一個return 否則錯誤

二分查找的方法介紹

?二分法就是設置兩個指針,讓它們一個在最左邊,一個在最右邊,而且要設置一個中間值然后為了查找有序數組中的值,不斷細分這個數組——假如你要尋找的這個數是小于中間值的,那么這個數就在左邊,這時候我們只需要在左邊再次重復操作——直到左邊指針和右邊指針找到了那個數(這個結束的條件有兩種情況)

那這樣寫第一遍你會發現——可能還是會發生錯誤——

在while循環中,結束的條件到底是left<right還是left<=right呢?

在循環中找到要繼續查找的是左邊或右邊后,right或left是等于middle(中間值)呢,還是其他呢?

那么接下來就介紹一個概念——循環不變量?

在整個過程中我們這個數組在查找時,是把它看成左閉右閉還是左閉右開呢?這個需要你在循環之前需要搞清楚的量,就是所謂的循環不變量

左閉右閉時——

  • 1.while括號里的條件為——left<=right因為這時left=right對于閉區間來說,是成立的
  • 2.而當需要移動指針時,right或者是left是直接等于middle-1/middle+1的,因為已經判斷好了middle上不是需要找的數.

左閉右開時——

  • 1.while括號里的條件為——left<right因為這時left=right對于半開區間來說,是不成立的
  • 2.而當需要移動指針時,right是直接等于middle的,因為這時候right是一個開區間,只有放在已查看過的middle,才不至于略掉其他的可能為你要尋找的數

學會之后:第二次可能寫出現的問題——

對于左閉右開的形式,從一開始就需要做出改變

——

1.定義時,右邊的要定義的比左閉右閉多一位

2.對于左邊的變到中間,是中間+1,因為這里是閉區間

3.對于右邊的變到中間,就是中間了

  • 更多有關二分查找的題(也來自代碼隨想錄)
  • 35.搜索插入位置(opens new window)
  • 34.在排序數組中查找元素的第一個和最后一個位置(opens new window)
  • 69.x 的平方根(opens new window)
  • 367.有效的完全平方數(opens new window)

這里只記錄一個二分查找擴展——搜索插入位置

暴力解法

class Solution {
public:int searchInsert(vector<int>& nums, int target) {for (int i = 0; i < nums.size(); i++) {// 分別處理如下三種情況// 目標值在數組所有元素之前// 目標值等于數組中某一個元素// 目標值插入數組中的位置if (nums[i] >= target) { // 一旦發現大于或者等于target的num[i],那么i就是我們要的結果return i;}}// 目標值在數組所有元素之后的情況return nums.size(); // 如果target是最大的,或者 nums為空,則返回nums的長度}
};

二分法(這里采用c++其中的vector<int>& nums相當于C語言中傳入一個整型數組nums,nums.size()返回這個數組的大小)

class Solution {
public:int searchInsert(vector<int>& nums, int target) {int n = nums.size();int left = 0;int right = n; // 定義target在左閉右開的區間里,[left, right)  targetwhile (left < right) { // 因為left == right的時候,在[left, right)是無效的空間int middle = left + ((right - left) >> 1);if (nums[middle] > target) {right = middle; // target 在左區間,在[left, middle)中} else if (nums[middle] < target) {left = middle + 1; // target 在右區間,在 [middle+1, right)中} else { // nums[middle] == targetreturn middle; // 數組中找到目標值的情況,直接返回下標}}// 分別處理如下四種情況// 目標值在數組所有元素之前 [0,0)// 目標值等于數組中某一個元素 return middle// 目標值插入數組中的位置 [left, right) ,return right 即可// 目標值在數組所有元素之后的情況 [left, right),因為是右開區間,所以 return rightreturn right;}
};

移除元素

題目:. - 力扣(LeetCode)

第一遍練習會出現的問題——

對于i不知道如何讓它再次看之前的數值——i--即可

第二次循環時,需要覆蓋,這時候要設置j=i+1,n[j]=n[j-1]

?方法一——暴力解題法

對于移除元素,在循環中找到所滿足條件的數組元素,然后覆蓋其位置數值(用循環)……

方法二——快慢指針法

首先來介紹一下什么是快指針和慢指針:

具體方法思路——

首先假設數組為[3,2,2,3]需要去除的值是3

快慢指針初始均指向第一個數,慢指針是為了獲取最終數組,它這個位置假如遇到需要去除的值,則要覆蓋——快指針獲取這個覆蓋的值,即最后把所有的需要去除的數給去除。

所以循環是現判斷快指針指向的位置是否為需要去除的值——是則往下移一位,不是則把這個值賦給慢指針所指向的值,然后慢指針往后移,循環往復,直到快指針遍歷結束

例子:假如第一個遇到的就是需要去除的值,那么快指針會先往下一步,然后如果所指向的值不為去除的值——賦值給慢指針,這樣可以覆蓋掉第一個需要去除的值,然后慢指針往后一步,如果慢指針還是指向需要去除的值,則重復上述步驟,這樣直到最后,快指針可以把所有的不需要去除的值給按順序覆蓋到需要去除的地方——完成代碼即可

int removeElement(int* nums, int numsSize, int val) {int s=0;int q=0;for(;q<numsSize;q++){if(nums[q]!=val){nums[s]=nums[q];s++;}}return s;
}

注意寫代碼時——這里的快慢指針不是真的指針,而是設置的數組下標。

有序數組的平方

題目:. - 力扣(LeetCode)

第一次暴力解題會出現的問題——

1.平方的for循環要i從0——numsSize(<號的時候)

2.排序方式——這里用選擇排序示例:

for(int i=0;i<numsSize-1;i++){for(int j=i+1;j<numsSize;j++){if(nums[i]>nums[j]){int t=nums[i];nums[i]=nums[j];nums[j]=t;}}}

第二次用雙指針寫出現的問題——

1.新建的數組需要動態開辟空間?

雙指針法

設計思路:

首先先觀察題目——發現如果平方之后,最大值一定是最左邊或是最右邊的平方,而且是按一定順序排列的數組,如果是右邊的平方大,則下一個就是左邊的平方,然后再往里比較,因此這里借助兩個指針,一左一右,進行大小比較,然后把大的一個賦值給新數組

?雙指針法的具體思路:

首先先創造一個新數組——和原來的數組一樣大小,這里使用動態數組的方式,然后設置兩個int類型的數據,它們分別為0和數組大小-1,然后比較這兩個所指的原數組數據平方誰大——誰就把這個放進新數組的最右邊,然后這個指針往中間的方向移動——再次比較——再次賦值給新數組的“指針”……直到兩個指針相遇,即可結束循環——代碼也就完成了

代碼:(result是新數組)

class Solution {
public:vector<int> sortedSquares(vector<int>& A) {int k = A.size() - 1;vector<int> result(A.size(), 0);for (int i = 0, j = A.size() - 1; i <= j;) { if (A[i] * A[i] < A[j] * A[j])  {result[k--] = A[j] * A[j];j--;}else {result[k--] = A[i] * A[i];i++;}}return result;}
};

長度最小的子數組

題目:

. - 力扣(LeetCode)

第一次寫出現的問題——

1.沒有思路

2.返回值?

3.這個int類型的最大值不知道是怎么寫?

INT32_MAX

?這里的思路其實看過代碼之后就有點恍然大悟了,但是不知道怎么想的,很妙

這道題依然采用兩個指針的方法——不過這里方法叫滑動窗口,因為可能指針的移動像滑動吧🤭

1.由于返回的是長度最小的滿足情況的數組大小——因此我們先把這個長度設置為最大——也就是int的最大,用INT32_MAX來表示,如果最后結果仍未這個值,說明沒有滿足條件的情況,返回0,否則返回長度的最小值。

2.這里使用雙指針的原因是——整個數組的滿足范圍不限制,而其中的每一組組合都可能滿足,如果想要簡便的實現的話,需要兩個指針,它們不斷變換,我們求其中的總值,如果滿足情況,則賦值給返回值,最后不斷更新返回值的較小值,就可找到返回值最小的情況,題目也就滿足了

滑動窗口的代碼實現:

這里兩個分別是指針1和指針2(剛開始都在同一位置),需要之間的值大于7,先設置一個sum的初始值為0,進入循環中,加上n[j],如果這時候的sum>=7,則直接給result賦值1,如果不是——指針j往下移動,然后循環內又再加上n[j]……直到指針指向第二個2時,sum>=7了,則先賦值給result一個較小值,然后指針1開始移動——尋找這個滿足條件的區間內可以有更小的可能嗎?(這時候需要減去n[i]后移動這個指針),然后發現從3——2區間也成立,然后再循環,直到指針循環到最后,result自然=最小值

代碼:

class Solution {
public:int minSubArrayLen(int s, vector<int>& nums) {int result = INT32_MAX;int sum = 0; // 滑動窗口數值之和int i = 0; // 滑動窗口起始位置int subLength = 0; // 滑動窗口的長度for (int j = 0; j < nums.size(); j++) {sum += nums[j];// 注意這里使用while,每次更新 起始位置,并不斷比較子序列是否符合條件while (sum >= s) {subLength = (j - i + 1); // 取子序列的長度result = result < subLength ? result : subLength;sum -= nums[i++]; // 這里體現出滑動窗口的精髓之處,不斷變更i(子序列的起始位置)}}// 如果result沒有被賦值的話,就返回0,說明沒有符合條件的子序列return result == INT32_MAX ? 0 : result;}

?這個時間復雜度為O(n)——這個是看每一個元素被操作的次數,因為有兩個指針,每個元素在滑動窗進來一次出去一次,時間復雜度為2n——O(n)

螺旋矩陣

題目:59. 螺旋矩陣 II - 力扣(LeetCode)

這個和二分法一樣,需要有不變量——左閉右開,

而這里發生錯誤的地方在于——

1.要知道每一個矩陣需要繞的圈數——n/2,

2.注意一圈過后,它的起始位置發生改變,要都加1

3.如果n為奇數的話,最后要判斷一下,單獨把中間的值賦值

?注意如何在c語言中動態初始化二維數組:

int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){//初始化返回的結果數組的大小*returnSize = n;*returnColumnSizes = (int*)malloc(sizeof(int) * n);//初始化返回結果數組ansint** ans = (int**)malloc(sizeof(int*) * n);
}

復習——malloc和free

free參數要么是NULL要么是之前從malloc、calloc、realloc返回的值,無返回值

malloc返回一塊連續的數組,返回類型為void*,C和C++規定,void*類型可以強制轉換為任何其他類型的指針

而malloc里面放的是需要空間的大小,一般用sizeof來計算?

注意:

要檢查所請求的內存是否分配成功

必須要釋放

可以使用exit(1)——用于退出整個程序,終止進程,返回1代表異常終止,返回0正常退出

?方法——模擬行為,這里重要的就是考慮循環不變量,而除此之外其它的也很重要——其中的思想,模擬過程

這其實是一個二維數組的初始化,不過更為復雜,這里看代碼——

class Solution {
public:vector<vector<int>> generateMatrix(int n) {vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定義一個二維數組int startx = 0, starty = 0; // 定義每循環一個圈的起始位置int loop = n / 2; // 每個圈循環幾次,例如n為奇數3,那么loop = 1 只是循環一圈,矩陣中間的值需要單獨處理int mid = n / 2; // 矩陣中間的位置,例如:n為3, 中間的位置就是(1,1),n為5,中間位置為(2, 2)int count = 1; // 用來給矩陣中每一個空格賦值int offset = 1; // 需要控制每一條邊遍歷的長度,每次循環右邊界收縮一位int i,j;while (loop --) {i = startx;j = starty;// 下面開始的四個for就是模擬轉了一圈// 模擬填充上行從左到右(左閉右開)for (j = starty; j < n - offset; j++) {res[startx][j] = count++;}// 模擬填充右列從上到下(左閉右開)for (i = startx; i < n - offset; i++) {res[i][j] = count++;}// 模擬填充下行從右到左(左閉右開)for (; j > starty; j--) {res[i][j] = count++;}// 模擬填充左列從下到上(左閉右開)for (; i > startx; i--) {res[i][j] = count++;}// 第二圈開始的時候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)startx++;starty++;// offset 控制每一圈里每一條邊遍歷的長度offset += 1;}// 如果n為奇數的話,需要單獨給矩陣最中間的位置賦值if (n % 2) {res[mid][mid] = count;}return res;}
};
  • 時間復雜度 O(n^2): 模擬遍歷二維矩陣的時間
  • 空間復雜度 O(1)

總結

這里的數組題目公涉及了三種方法——

二分法:注意循環不變量

雙指針法:通過一個快指針和慢指針在一個for循環下完成兩個for循環的工作。

滑動窗口:滑動窗口的精妙之處在于根據當前子序列和大小的情況,不斷調節子序列的起始位置。從而將O(n^2)的暴力解法降為O(n)

最好的方法是多用多看多練,而且要及時總結錯誤及思路。

感謝觀看完畢,歡迎點贊收藏😉

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

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

相關文章

Java學習—FileInputStream

在Java編程中&#xff0c;文件操作是日常任務之一。無論是讀取配置文件、處理圖像&#xff0c;還是讀寫日志文件&#xff0c;理解如何有效地進行文件讀取都是非常重要的。Java提供了多種方式來操作文件&#xff0c;而FileInputStream是其中最基礎也是最直接的一種。本文將深入探…

Spring面試系列-01

1. 什么是 Spring 框架? Spring中文翻譯過來是春天的意思,被稱為J2EE的春天,是一個開源的輕量級的Java開發框架, 具有控制反轉(IoC)和面向切面(AOP)兩大核心。Java Spring框架通過聲明式方式靈活地進行事務的管理,提高開發效率和質量。 Spring框架不僅限于服務器端的…

three 層級模型

group.remove(mesh1,mesh2);Vector3與模型位置、縮放屬性 Group層級模型(樹結構) 創建了兩個網格模型mesh1、mesh2&#xff0c;通過THREE.Group類創建一個組對象group,然后通過add方法把網格模型mesh1、mesh2作為設置為組對象group的子對象&#xff0c;然后在通過執行scene.a…

jenkins部署maven項目

流程&#xff1a; jenkins從代碼倉庫讀取代碼&#xff0c;將代碼文件放入jenkins的工作空間&#xff0c;將jenkins工作空間的代碼進行打包&#xff0c;將jar包遠程發送給服務器。 一&#xff1a;所需插件二&#xff1a;Tools 三&#xff1a;System&#xff1a; 配置ssh連接的…

github要求2fa身份驗證

前言 github登陸的時候發現要求2fa驗證, 2fa是啥?咋驗證? 解決 2FA&#xff08;Two-Factor Authentication&#xff0c;雙因素身份驗證&#xff09; 就是在賬戶和密碼的基礎上增加一次驗證碼驗證,這樣即使密碼被竊取,由于黑客沒有你的驗證碼也無法登陸 就像是銀行的u盾一樣…

python63-Python的循環之循環使用else

Python的循環都可以定義else代碼塊&#xff0c;當循環條件為False 時&#xff0c;程序會執行else代碼塊。如下代碼示范了為while循環定義else代碼塊。 # !/usr/bin/env python# -*- coding: utf-8 -*-# Time : 2024/01# Author : Laopicount_i 0while count_i < 5:print(c…

Java集合相關面試題(2024大廠高頻面試題系列)

1、說一說Java提供的常見集合&#xff1f;&#xff08;畫一下集合結構圖&#xff09; 在java中提供了量大類的集合框架&#xff0c;主要分為兩類&#xff1a; 第一個是Collection 屬于單列集合&#xff0c;第二個是Map 屬于雙列集合 在Collection中有兩個子接口List和Set。…

【力扣hot100】刷題筆記Day14

前言 又是新的一周&#xff0c;快樂的周一&#xff0c;快樂地刷題&#xff0c;今天把鏈表搞完再干活&#xff01; 114. 二叉樹展開為鏈表 - 力扣&#xff08;LeetCode&#xff09; 前序遍歷 class Solution:def flatten(self, root: Optional[TreeNode]) -> None:if not r…

回溯 Leetcode 51 N皇后

N皇后 Leetcode 51 學習記錄自代碼隨想錄 按照國際象棋的規則&#xff0c;皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。 n 皇后問題 研究的是如何將 n 個皇后放置在 nn 的棋盤上&#xff0c;并且使皇后彼此之間不能相互攻擊。 給你一個整數 n &#xff0c;返回所…

Linux —— 鏈接文件

硬鏈接 一般情況下&#xff0c;文件名和inode號碼是"一一對應"關系&#xff0c;每個inode號碼對應一個文件名。但是&#xff0c;Unix/Linux系統允許&#xff0c;多個文件名指向同一個inode號碼。 這意味著&#xff0c;可以用不同的文件名訪問同樣的內容&#xff1b;對…

軟件開發常見模型解析

軟件開發常見模型解析 摘要&#xff1a;本文將為您詳細介紹軟件開發過程中常見的幾種模型&#xff0c;包括瀑布模型、敏捷開發模型、螺旋模型、迭代模型和原型模型。通過了解這些模型的原理、優缺點&#xff0c;幫助您在不同的軟件項目中選擇最適合的開發模型。 一、引言 在…

【IC前端虛擬項目】inst_buffer子模塊DS與RTL編碼

【IC前端虛擬項目】數據搬運指令處理模塊前端實現虛擬項目說明-CSDN博客 需要說明一下的是,在我所提供的文檔體系里,并沒有模塊的DS文檔哈,因為實際項目里我也不怎么寫DS畢竟不是每個公司都和HISI一樣對文檔要求這么嚴格的。不過作為一個培訓的虛擬項目,還是建議在時間充裕…

Docker技術概論(3):Docker 中的基本概念

Docker技術概論&#xff08;3&#xff09; Docker 中的基本概念 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://…

基于java+springboot女士電商平臺系統源碼+文檔設計

基于javaspringboot女士電商平臺系統源碼文檔設計 博主介紹&#xff1a;多年java開發經驗&#xff0c;專注Java開發、定制、遠程、文檔編寫指導等,csdn特邀作者、專注于Java技術領域 作者主頁 央順技術團隊 Java畢設項目精品實戰案例《1000套》 歡迎點贊 收藏 ?留言 文末獲取源…

C語言----動態內存管理(2)

1.這里總結動態內存管理里面的錯誤 &#xff08;1&#xff09;使用malloc開辟空間以后直接賦值 這個就是malloc開辟失敗返回空指針&#xff0c;直接給空指針賦值就是錯誤的&#xff0c; tip1:使用malloc開辟空間以后一定要判斷是否為空 &#xff08;2&#xff09; 越界訪問…

Python批量提取文件夾中圖片的名稱及路徑到指定的.txt文件中

目錄 一、代碼二、提取效果 一、代碼 import os# 定義要保存的文件名 file_name "TestImage/Image_Visible_Gray.txt"# 讀取文件夾路徑 folder_path "TestImage/Image_Visible_Gray"# 遍歷文件夾中的所有文件 with open(file_name, "w") as f…

IO進程線程day1

編寫鏈表&#xff0c;鏈表里面隨便搞點數據&#xff0c;使用fprintf將鏈表中所有的數據保存到文件中&#xff0c;用fscanf讀取文件中的數據寫入鏈表中 #include <stdio.h> #include <stdlib.h>typedef struct Node {int data;struct Node* next; } Node;// 創建新…

可移植性(兼容性)測試指南

可移植性是指應用程序能夠安裝到不同的環境中&#xff0c;在不同的環境中使用&#xff0c;甚至可以移動到不同的環境中。當然&#xff0c;前兩者對所有系統都很重要。就PC軟件而言&#xff0c;鑒于操作系統、共存和互操作應用程序、硬件、帶寬可用性等方面的快速變化&#xff0…

抖店如何運營?新手應該怎么做?從入門到精通詳細講解!

我是電商珠珠 做抖店必須先搞懂它的基礎流程&#xff0c;流程搞懂了&#xff0c;才能有進一步的可能。不要急功近利&#xff0c;想要一口吃個胖子&#xff0c;這樣做就會直接造成店鋪被清店&#xff0c;扣除保證金&#xff0c;甚至還會埋怨自己沒用。 我做電商已經三年多的時…

vue3 日期延后一天

問題&#xff1a;提交信息時要求將所選日期延后一天進行提交解決過程&#xff1a;1.定義延后一天的計算方法&#xff0c;在提交前&#xff0c;將提交日期傳入調用該方法 2.對延后的日期進行格式化&#xff0c;最后格式為yy-mm-dd解決結果&#xff1a; const…