劍指offer第2版:雙指針+排序+分治+滑動窗口

一、p129-JZ21使奇數位于偶數前面(不考慮相對位置)(hoare快排雙指針)

調整數組順序使奇數位于偶數前面(二)_牛客題霸_牛客網

? ? ? ? ?如果不考慮相對位置的話,那么我們可以模仿hoare快排,使用雙指針的思想,一個指針在前向后找偶數,一個指針在后,向前找奇數,然后再交換就行 時間復雜度是n?

class Solution {public:vector<int> reOrderArrayTwo(vector<int>& nums) {//用雙指針int n = nums.size();if (n == 0) return{};int left = 0, right = n - 1;//左邊找偶數,右邊找奇數 然后交換while (left < right) {while (left < right && nums[left] & 1) ++left;while (left < right && (nums[right] & 1) == 0) --right;if (left < right) swap(nums[left], nums[right]);}return nums;}
};

二、p129-JZ21使奇數位于偶數前面(考慮相對位置)(插入排序)

調整數組順序使奇數位于偶數前面(一)_牛客題霸_牛客網

因為要考慮相對位置,所以我們就要借鑒一下插入排序的思想?

class Solution {public:vector<int> reOrderArray(vector<int>& nums) {//奇數位于偶數前面,并且還要保證相對位置不變//那就得用到插入排序的思想了! //從前往后 看到奇數我就想辦法往前挪//1 3 5 6 7int n = nums.size();int k = 0;for (int i = 0; i < n; ++i)if (nums[i] &1) { //從左向右,每次遇到的,都是最前面的奇數,一定將來要被放在k下標處int temp = nums[i]; //先將當前奇數保存起來int j = i;while (j >  k) { //將該奇數之前的內容(偶數序列),整體后移一個位置nums[j] = nums[j - 1];--j;}//將奇數保存在它將來改在的位置,因為我們是從左往右放的,沒有跨越奇數,所以一定是相對位置不變的nums[k++] =temp; }return nums;}
};

??擴展:如果面試官問你如果想要把題目改成將負數放在非負數前面,或者將可以被3整除的放在不能被3整除的數的前面,這個時候我們可以通過傳入仿函數來設計一套通用的解法,然后只需要在判斷的邏輯那里調用仿函數可以。這樣可以實現解耦成兩部分:一是判斷數字應該在數組的前半部分還是后半部分的標準,而是拆分數組的操作。

三、p249-JZ51數組中的逆序對(歸并排序)

數組中的逆序對_牛客題霸_牛客網

? ? 該題能夠使用歸并排序,本質是利用了待合并有序數組的單調性,我們在題目中需要去找到區間前的數和區間后的數存在某種大小關系的時候,我們就可以用分治思想中的歸并排序在兩段待排序的升序區間中根據單調性去找我們想要的結果。

升序的做法: 以后面區間為基點,找前面區間有多少數字比我大

class Solution {
public:
//升序 以l2為基準 找前面有多少數字比我大const int MOD=1e9+7;int InversePairs(vector<int>& nums) {int n=nums.size();vector<int> temp(n);return mergesort(nums,0,n-1,temp);}int mergesort(vector<int>& nums,int begin,int end,vector<int>& temp){if(begin>=end) return 0;int mid=(begin+end)>>1;int ret=mergesort(nums,begin,mid,temp)+mergesort(nums,mid+1,end,temp);int cur1=begin,cur2=mid+1,i=begin;while(cur1<=mid&&cur2<=end)if(nums[cur1]<=nums[cur2]) temp[i++]=nums[cur1++];else{ret+=mid-cur1+1;ret%=MOD;temp[i++]=nums[cur2++];}while(cur1<=mid) temp[i++]=nums[cur1++];while(cur2<=end) temp[i++]=nums[cur2++];//開始還原for(i=begin;i<=end;++i) nums[i]=temp[i];return ret;}
};

降序的做法: 以前面區間為基點,找后面區間有多少數字比我小

class Solution {
public:
//降序 以l1為基準 找后面有多少數字比我小const int MOD=1e9+7;int InversePairs(vector<int>& nums) {int n=nums.size();vector<int> temp(n);return mergesort(nums,0,n-1,temp);}int mergesort(vector<int>& nums,int begin,int end,vector<int>& temp){if(begin>=end) return 0;int mid=(begin+end)>>1;int ret=mergesort(nums,begin,mid,temp)+mergesort(nums,mid+1,end,temp);int cur1=begin,cur2=mid+1,i=begin;while(cur1<=mid&&cur2<=end)if(nums[cur1]<=nums[cur2]) temp[i++]=nums[cur2++];else{ret+=end-cur2+1;ret%=MOD;temp[i++]=nums[cur1++];}while(cur1<=mid) temp[i++]=nums[cur1++];while(cur2<=end) temp[i++]=nums[cur2++];//開始還原for(i=begin;i<=end;++i) nums[i]=temp[i];return ret;}
};

?四、p209-JZ40最小的k個數(快速選擇排序)

最小的K個數_牛客題霸_牛客網

方法1:建大堆 一個個和堆頂比較,最后剩下的k個數要找的(適合處理海量數據)

#include <queue>
class Solution {
public:vector<int> GetLeastNumbers_Solution(vector<int>& input, int k) {int n=input.size();if(k==0||n==0||k>n) return {};//搞一個大根堆  然后讓前k個進去  然后再一個個排除 最后剩下的就是最小的k個數priority_queue<int> q;for(int i=0;i<k;++i) q.push(input[i]);//前k個丟進去//然后開始排除for(int i=k;i<n;++i) if(input[i]<q.top()){q.pop();q.push(input[i]);}//插入到數組中返回vector<int> ret(k);for(int i=0;i<k;++i){ret[i]=q.top();q.pop();}return ret;}
};

方法2:快速選擇排序算法(適合單次查找且可以修改原數組的情況下)

#include <cstdlib>
class Solution {
public:vector<int> GetLeastNumbers_Solution(vector<int>& nums, int k) {srand((unsigned int)time(nullptr));//時間種子qsort(nums,0,nums.size()-1,k);return {nums.begin(),nums.begin()+k};}void qsort(vector<int>& nums,int begin,int end,int k){if(begin>=end) return;//要按照數組分三塊的思想int key=nums[rand()%(end-begin+1)+begin];int left=begin-1,right=end+1,cur=begin;while(cur<right)if(nums[cur]<key) swap(nums[cur++],nums[++left]);else if(nums[cur]>key) swap(nums[--right],nums[cur]);else ++cur;//根據區間大小進行選擇int a=left-begin+1,b=(right-1)-(left+1)+1;if(k<a) qsort(nums,begin,left,k);//說明在左邊區間里 繼續排序else if(k<=a+b) return;else qsort(nums,right,end,k-a-b);//說明在第三塊區間里}
};

五、p214-JZ41數據流中的中位數(優先級隊列)

數據流中的中位數_牛客題霸_牛客網

策略:優先級隊列大小堆維護中位數 ? add(logN) ?find(1)

設計思路:

1、建立left為大根堆,right為小根堆

2、我們的add控制始終保持left的數量要么和right相等,要么比right多一個,為了能夠滿足在O(1)的復雜度內完成找到中位數的任務,我們希望當left多一個的時候,left堆頂的元素就是中位數,而當left和right相等的時候,中位數就是兩個堆的堆頂元素的平均值。

3、為了達到這個目的,我們在時刻控制left和right的數量的同時,一定要保證left里面的元素是小于等于right里面的元素的,所以add要分兩種情況去討論:

情況1:當兩個堆的元素個數相等的時候

? ? (1)如果left為空,或者是add的元素比left的堆頂元素小,那么就讓該元素直接進left

? ? (2)如果add的元素比left的堆頂元素大,那么他也有可能會比right的元素大,所以我們必須要將這個元素丟到right中,但是直接丟就會破壞規則,所以我們要先將add的元素丟到right中進行調整,然后再將right的堆頂元素丟到left中去,保持left和right的數量關系。 (注意,這里的先后順序很重要,我們不能先將right的堆頂元素丟到left中,然后再將add丟到right中進行調整,因為我們只是知道這個數比left的堆頂元素大,但是他是比right的堆頂元素大還是小我們不得而知,必須要通過他自己的向下調整去選出來)

情況2:當left的元素比right多一個的時候

? (1)如果add的元素比left的堆頂元素大,這個時候無腦進右邊就行了。

? ?(2)如果add的元素比left的堆頂元素小,這個時候我們也得把add的元素丟到left中,然后為了保持數量關系,將調整過后的left的堆頂元素移到right中即可。

細節處理:

1、我們在比較的時候始終實用left的元素進行比較,因為左邊不為空的時候右邊也可能為空,所以我們如果不用left去比較而是用right去比較,那么還需要多考慮一種邊界情況。

2、雖然我們add的都是int類型,但是當兩個堆的元素個數相同的時候,我們去取兩個堆頂元素取平均值的,而平均值是有可能會出現小數的,所以如果我們還用int的話可能會造成小數點丟失,所以我們在/2的時候變成/2.0,這樣結果就會被強轉成double;

class Solution {
public:void Insert(int num) {//如果為空 直接插入左邊//如果兩邊一樣多,  若<=left 直接插左邊 若>right 就先插入右邊 然后再移堆頂int m=left.size(),n=right.size();if(m==n)if(left.empty()||num<=left.top()) left.push(num);else{//num>left>left.topright.push(num);left.push(right.top());right.pop();} //如果左邊比右邊多  else{//m==n+1if(num>left.top()) right.push(num);else{//num<=left.topleft.push(num);right.push(left.top());left.pop();}}}double GetMedian() { //如果左邊比右邊多 就返回左邊     如果相等就返回中間值if(left.size()>right.size()) return left.top();else return (left.top()+right.top())/2.0;}
private://需要有一個大根堆(在前面) 一個小根堆(在后面)priority_queue<int> left;//大根堆priority_queue<int,vector<int>,greater<int>> right;///右邊是小根堆
};

六、p227-JZ45把數組排成最小的數(重寫排序/冒泡排序)

把數組排成最小的數_牛客題霸_牛客網

方法一:重載比較的排序(推薦使用)

具體做法:

  • step 1:優先判斷空數組的特殊情況。
  • step 2:將數組中的數字元素轉換成字符串類型。
  • step 3:重載排序比較為字符串類型的x + y < y + x,然后進行排序。
  • step 4:將排序結果再按照字符串拼接成一個整體。
class Solution {
public:string PrintMinNumber(vector<int>& nums) {int n=nums.size();if(n==0) return"";vector<string> strs;strs.reserve(n);for(auto&num:nums) strs.emplace_back(to_string(num));sort(strs.begin(),strs.end(),[&strs](const string&s1,const string&s2){return s1+s2<s2+s1;});string ret;for(auto&s:strs) ret+=s;return ret;}
};

方法二:冒泡排序(擴展思路)

class Solution {
public:string PrintMinNumber(vector<int> numbers) {string res = "";//空數組的情況if(numbers.size() == 0)return res;vector<string> nums;//將數字轉成字符for(int i = 0; i < numbers.size(); i++)nums.push_back(to_string(numbers[i]));//冒泡排序for(int i = 0; i < nums.size() - 1; i++){for(int j = 0; j < nums.size() - i - 1; j++){string s1 = nums[j] + nums[j + 1];string s2 = nums[j + 1] + nums[j];//比較拼接的大小交換位置if(s1 > s2) swap(nums[j], nums[j + 1]);}}//字符串疊加for(int i = 0; i < nums.size(); i++)res += nums[i];return res;}
};

七、p280-JZ57和為S的兩個數字(雙指針+單調性)

和為S的兩個數字_牛客題霸_牛客網

思路:

我們能想到最直觀的解法,可能就是兩層遍歷,將數組所有的二元組合枚舉一遍,看看是否是和為目標值,但是這樣太費時間了,既然加法這么復雜,我們是不是可以嘗試一下減法:對于數組中出現的一個數a,如果目標值減去a的值已經出現過了,那這不就是我們要找的一對元組嗎?這種時候,快速找到已經出現過的某個值,可以考慮使用哈希表快速檢驗某個元素是否出現過這一功能。

具體做法:

  • step 1:構建一個哈希表,其中key值為遍歷數組過程中出現過的值
  • step 2:遍歷數組每個元素,如果目標值減去該元素的結果在哈希表中存在,說明我們先前遍歷的時候它出現過,根據保存的值,就可以得到結果。
  • step 3:如果相減后的結果沒有在哈希表中,說明先前遍歷的元素中沒有它對應的另一個值,那我們將它加入哈希表,等待后續它匹配的那個值出現即可。
class Solution {
public:vector<int> FindNumbersWithSum(vector<int> nums,int sum) {unordered_set<int> hash; //在哈希表中查找sum-array[i]for(auto&e:nums){int temp=sum-e;if(hash.find(temp)==hash.end()) hash.insert(e);else return {temp,e};}return {};}
};

思路:

這道題目還有一個條件是數組是升序序列,在方法一中沒有用到。這個條件有什么用?既然數組是有序的,那我們肯定知道和找到一定程度就不找了,我們為什么要從最小的兩個數開始相加呢?我們可以用二分法的思路,從中間開始找。

使用雙指針指向數組第一個元素和最后一個元素,然后雙指針對撞移動,如果兩個指針下的和正好等于目標值sum,那我們肯定找到了,如果和小于sum,說明我們需要找到更大的,那只能增加左邊的元素,如果和大于sum,說明我們需要找更小的,只能減小右邊的元素。

具體做法:

  • step 1:準備左右雙指針分別指向數組首尾元素。
  • step 2:如果兩個指針下的和正好等于目標值sum,則找到了所求的兩個元素。
  • step 3:如果兩個指針下的和大于目標值sum,右指針左移;如果兩個指針下的和小于目標值sum,左指針右移。
  • step 4:當兩指針對撞時,還沒有找到,就是數組沒有。
class Solution {
public:vector<int> FindNumbersWithSum(vector<int> nums,int sum) {//雙指針int left=0,right=nums.size()-1;while(left<right){int x=nums[left]+nums[right];if(x<sum) ++left;else if(x==sum) return{nums[left],nums[right]};else --right;}return {};}
};

八、擴展p282-JZ57 和為S的連續正數序列(滑動窗口)

?和為S的連續正數序列_牛客題霸_牛客網

思路:

從某一個數字開始的連續序列和等于目標數如果有,只能有一個,于是我們可以用這個性質來使區間滑動。

兩個指針l、r指向區間首和區間尾,公式(l+r)?(r?l+1)/2(l+r)?(r?l+1)/2計算區間內部的序列和,如果這個和剛好等于目標數,說明以該區間首開始的序列找到了,記錄下區間內的序列,同時以左邊開始的起點就沒有序列了,于是左區間收縮;如果區間和大于目標數,說明該區間過長需要收縮,只能收縮左邊;如果該區間和小于目標數,說明該區間過短需要擴大,只能向右擴大,移動區間尾。

具體做法:

  • step 1:從區間[1,2][1,2]開始找連續子序列。
  • step 2:每次用公式計算區間內的和,若是等于目標數,則記錄下該區間的所有數字,為一種序列,同時左區間指針向右。
  • step 3:若是區間內的序列和小于目標數,只能右區間擴展,若是區間內的序列和大于目標數,只能左區間收縮。
class Solution {
public:vector<vector<int>> FindContinuousSequence(int n) {//滑動窗口if(n<3) return {};vector<vector<int>> ret;//結果集vector<int> path;//存儲結果for(int left=1,right=2,total=3;left<right;total-=left,++left){while(total<n){++right;total+=right;}if(total==n){for(int i=left;i<=right;++i)  path.emplace_back(i);ret.emplace_back(path);path.clear();//清空}}return ret;}
};

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

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

相關文章

14-C語言:第14天筆記

C語言&#xff1a;第14天筆記 內容提要 指針 變量指針與指針變量 指針變量做函數參數指針變量指向數組元素 數組指針與指針數組 數組指針回顧 變量指針與指針變量 變量指針&#xff1a;變量的地址值&#xff08;首地址&#xff09;&#xff0c;本質是指針、地址 指針變量&#…

【筆記】活度系數推導

文章目錄一、理想溶液的假設與局限性1.1 理想溶液的定義1.2 理想溶液的局限性二、活度與活度系數的引入2.1 活度的定義2.2 修正后的化學勢表達式三、活度系數的物理意義四、為什么需要活度系數&#xff1f;4.1 理論需求4.2 擴散理論中的必要性五、活度系數的具體作用5.1 在化學…

基于Docker的GPU版本飛槳PaddleOCR部署深度指南(國內鏡像)2025年7月底測試好用:從理論到實踐的完整技術方案

還是網上沒找到這個基于Docker的GPU版本飛槳PaddleOCR部署教程&#xff0c;于是就有了這一篇。 這個的確坑很多&#xff0c;可能后面變一個版本就不好用了&#xff0c;也是為什么這篇博客不完全粘貼代碼的原因。 端口是示例&#xff0c;可以隨意改。在人工智能與文檔數字化高速…

Python-初學openCV——圖像預處理(三)

目錄 一、邊緣填充 1、邊界復制 2、邊界反射 3、邊界反射101 4、邊界常數 5、邊界包裹 二、透視變換 三、圖像掩膜 1、制作掩膜 2、與運算 3、顏色替換 四、ROI切割 五、圖像添加水印 一、邊緣填充 我們對圖像進行處理后&#xff0c;需要對空出來的區域進行一個填充…

【ESP32設備通信】-W5500與ESP32 /ESP32 S3集成

W5500與ESP32 /ESP32 S3集成 文章目錄 W5500與ESP32 /ESP32 S3集成 1、W5500介紹 2、硬件準備與接線 3、代碼實現 3.1 以太網設置 3.2 簡單HTTP請求 3.3 HTTPS請求 3.4 查詢證書 ESP32 憑借其強大的 Wi-Fi 功能,一直是物聯網項目的熱門選擇。ESP32 現在支持帶有 SSL 的原生以太…

vue - 使用canvas繪制驗證碼

封裝繪制驗證碼 verify-code.vue<template><div class"captcha"><canvas ref"canvasRef" :width"width" :height"height" click"refreshCaptcha"></canvas></div> </template><scri…

[10月考試] F

[10月考試] F 題目描述 給定長度為 nnn 的序列 ana_nan?&#xff0c;保證 aia_iai? 為非負整數。 mmm 次詢問&#xff0c;每次給定區間 l,rl,rl,r&#xff0c;求出 al,al1,…,ara_l,a_{l1},\ldots,a_ral?,al1?,…,ar? 的 mexmexmex。 對于一個序列&#xff0c;定義其 mexm…

收集了全球55個AI寫作工具

我們即將推出一整套AI生產力工具矩陣&#xff0c;覆蓋內容創作&#xff08;AI寫作助手&#xff09;、視覺設計&#xff08;智能圖像處理&#xff09;、音視頻制作&#xff08;自動轉錄與編輯&#xff09;及智能編程等多個核心領域。這些解決方案通過先進的機器學習算法&#xf…

Elastic 勞動力的生成式 AI:ElasticGPT 的幕后解析

作者&#xff1a;來自 Elastic Jay Shah, Adhish Thite ElasticGPT — 由 Elastic 提供支持&#xff0c;專為 Elastic 打造 ElasticGPT 是我們基于檢索增強生成&#xff08;RAG&#xff09;框架構建的內部生成式 AI &#xff08;GenAI&#xff09;助手。它是使用 Elastic 自有…

CS231n-2017 Assignment1

KNN&#xff1a;這里要求我們完成一個KNN分類器&#xff0c;實現對圖片使用KNN算法進行分類標簽k_nearest_neighbor.py這里要求我們完成4個接口# X:測試集 # 使用兩個循環 def compute_distances_two_loops(self, X):num_test X.shape[0]num_train self.X_train.shape[0]dist…

[python][flask]Flask-Principal 使用詳解

Flask-Principal 是一個專為 Flask 應用設計的身份管理和權限控制擴展。它能夠幫助開發者輕松實現用戶身份驗證和權限管理&#xff0c;從而提升應用的安全性和用戶體驗。該項目最初由 Ali Afshar 開發&#xff0c;現已成為 Pallets 社區生態系統的一部分&#xff0c;由社區共同…

抖音與B站爬蟲實戰,獲取核心數據

本文將深入講解兩大主流短視頻平臺&#xff08;抖音、B站&#xff09;的爬蟲實戰技術&#xff0c;提供可直接運行的代碼解決方案&#xff0c;并分享突破反爬機制的核心技巧。一、平臺特性與爬蟲難點對比平臺數據價值主要反爬措施推薦抓取方式抖音視頻數據、用戶畫像、熱榜簽名驗…

WSL切換網絡模式

WSL切換網絡模式問題WSL從NAT改成MIRRORED找到WSL Setting修改配置重啟電腦&#xff08;注意不是重啟WSL&#xff09;運行pio run驗證IP問題 從魚香ROS買了一個小魚車&#xff0c;開始學習&#xff0c;然而裝環境都要搞死我了。 垃圾VirtualBox我新買的電腦&#xff0c;裝個Vi…

[Linux入門] Linux 遠程訪問及控制全解析:從入門到實戰

目錄 一、SSH 遠程管理&#xff1a;為什么它是遠程訪問的首選&#xff1f; 1??什么是 SSH&#xff1f; 2??SSH 為什么比傳統工具更安全&#xff1f; 3??SSH 的 “三大組成部分” 4??SSH 工作的 “五步流程” 5??常用 SSH 工具 二、實戰&#xff1a;構建 SSH 遠…

n8n AI資訊聚合與分發自動化教程:從數據獲取到微信與Notion集成

引言 n8n簡介&#xff1a;自動化工作流利器 n8n是一款功能強大的開源自動化工具&#xff0c;采用獨特的“公平代碼”&#xff08;Fair-Code&#xff09;許可模式&#xff0c;旨在幫助用戶連接各種應用程序和服務&#xff0c;從而實現工作流的自動化。它通過直觀的可視化界面&am…

遞歸查詢美國加速-技術演進與行業應用深度解析

在當今數據驅動的時代&#xff0c;遞歸查詢已成為處理層級數據的核心技術&#xff0c;尤其在美國科技領域獲得廣泛應用。本文將深入解析遞歸查詢在美國加速發展的關鍵因素&#xff0c;包括技術演進、行業應用場景以及性能優化策略&#xff0c;幫助讀者全面理解這一重要技術趨勢…

【AIGC專欄】WebUI實現圖片的縮放

圖片的縮放包含如下的各類不同的縮放模型。 Lanczos Lanczos重采樣是一種數學上精確的方法,用于圖像放大或縮小。它使用了一種稱為 sinc 函數的數學公式,可以在保留圖像細節的同時減少鋸齒效應。 Nearest 最近鄰插值是一種簡單的圖像放大方法,通過復制最近的像素值來填充新…

Libevent(4)之使用教程(3)配置

Libevent(4)之使用教程(3)配置事件 Author: Once Day Date: 2025年7月27日 一位熱衷于Linux學習和開發的菜鳥&#xff0c;試圖譜寫一場冒險之旅&#xff0c;也許終點只是一場白日夢… 漫漫長路&#xff0c;有人對你微笑過嘛… 本文檔翻譯于&#xff1a;Fast portable non-bl…

若依前后端分離版學習筆記(三)——表結構介紹

前言&#xff1a; 這一節將ruoyi框架中數據庫中的表結構過一遍&#xff0c;查看都有哪些表及其表結構及關聯關系&#xff0c;為后續代碼學習做準備。 一 代碼生成表記錄代碼生成的業務表及相關字段1 代碼生成業務表 CREATE TABLE gen_table (table_id bigint(20) NOT NULL AUTO…

NFS服務安裝與使用

概述 內網需要使用NFS服務掛載到其他服務器&#xff0c;用做數據備份使用。 安裝 # Centos yum install -y nfs-utils # Ubuntu apt install nfs-common配置 # 編輯 vim /etc/exports # 輸入內容 /public/KOL-ESbackup 172.29.1.0/24 192.168.8.63 192.168.8.64 192.168.8.65(r…