「leetcode」93.復原IP地址【回溯演算法】詳解!
本文 https://github.com/youngyangyang04/leetcode-master 已經收錄,裡面還有leetcode刷題攻略、各個型別經典題目刷題順序、思維導圖,可以fork到自己倉庫,有空看一看一定會有所收穫,如果對你有幫助也給一個star支援一下吧!
93.復原IP地址
題目地址:https://leetcode-cn.com/problems/restore-ip-addresses/
給定一個只包含數字的字串,復原它並返回所有可能的 IP 地址格式。
有效的 IP 地址 正好由四個整數(每個整數位於 0 到 255 之間組成,且不能含有前導 0),整數之間用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 無效的 IP 地址。
示例 1:
輸入:s = “25525511135”
輸出:[“255.255.11.135”,“255.255.111.35”]
示例 2:
輸入:s = “0000”
輸出:[“0.0.0.0”]
示例 3:
輸入:s = “1111”
輸出:[“1.1.1.1”]
示例 4:
輸入:s = “010010”
輸出:[“0.10.0.10”,“0.100.1.0”]
示例 5:
輸入:s = “101023”
輸出:[“1.0.10.23”,“1.0.102.3”,“10.1.0.23”,“10.10.2.3”,“101.0.2.3”]
提示:
0 <= s.length <= 3000
s 僅由數字組成
思路
做這道題目之前,最好先把回溯演算法:分割回文串這個做了。
這道題目相信大家剛看的時候,應該會一臉茫然。
其實只要意識到這是切割問題,切割問題就可以使用回溯搜尋法把所有可能性搜出來,和剛做過的回溯演算法:分割回文串就十分類似了。
切割問題可以抽象為樹型結構,如圖:
回溯三部曲
- 遞迴引數
在回溯演算法:分割回文串中我們就提到切割問題類似組合問題。
startIndex一定是需要的,因為不能重複分割,記錄下一層遞迴分割的起始位置。
本題我們還需要一個變數pointNum,記錄新增逗點的數量。
所以程式碼如下:
vector<string> result;// 記錄結果
// startIndex: 搜尋的起始位置,pointNum:新增逗點的數量
void backtracking(string& s, int startIndex, int pointNum) {
- 遞迴終止條件
終止條件和回溯演算法:分割回文串情況就不同了,本題明確要求只會分成4段,所以不能用切割線切到最後作為終止條件,而是分割的段數作為終止條件。
pointNum表示逗點數量,pointNum為3說明字串分成了4段了。
然後驗證一下第四段是否合法,如果合法就加入到結果集裡
程式碼如下:
if (pointNum == 3) { // 逗點數量為3時,分隔結束
// 判斷第四段子字串是否合法,如果合法就放進result中
if (isValid(s, startIndex, s.size() - 1)) {
result.push_back(s);
}
return;
}
- 單層搜尋的邏輯
在回溯演算法:分割回文串中已經講過在迴圈遍歷中如何擷取子串。
在for (int i = startIndex; i < s.size(); i++)
迴圈中 [startIndex, i]這個區間就是擷取的子串,需要判斷這個子串是否合法。
如果合法就在字串後面加上符號.
表示已經分割。
如果不合法就結束本層迴圈,如圖中剪掉的分支:
然後就是遞迴和回溯的過程:
遞迴呼叫時,下一層遞迴的startIndex要從i+2開始(因為需要在字串中加入了分隔符.
),同時記錄分割符的數量pointNum 要 +1。
回溯的時候,就將剛剛加入的分隔符.
刪掉就可以了,pointNum也要-1。
程式碼如下:
for (int i = startIndex; i < s.size(); i++) {
if (isValid(s, startIndex, i)) { // 判斷 [startIndex,i] 這個區間的子串是否合法
s.insert(s.begin() + i + 1 , '.'); // 在i的後面插入一個逗點
pointNum++;
backtracking(s, i + 2, pointNum); // 插入逗點之後下一個子串的起始位置為i+2
pointNum--; // 回溯
s.erase(s.begin() + i + 1); // 回溯刪掉逗點
} else break; // 不合法,直接結束本層迴圈
}
判斷子串是否合法
最後就是在寫一個判斷段位是否是有效段位了。
主要考慮到如下三點:
- 段位以0為開頭的數字不合法
- 段位裡有非正整數字符不合法
- 段位如果大於255了不合法
程式碼如下:
// 判斷字串s在左閉又閉區間[start, end]所組成的數字是否合法
bool isValid(const string& s, int start, int end) {
if (start > end) {
return false;
}
if (s[start] == '0' && start != end) { // 0開頭的數字不合法
return false;
}
int num = 0;
for (int i = start; i <= end; i++) {
if (s[i] > '9' || s[i] < '0') { // 遇到非數字字元不合法
return false;
}
num = num * 10 + (s[i] - '0');
if (num > 255) { // 如果大於255了不合法
return false;
}
}
return true;
}
C++程式碼
根據關於回溯演算法,你該瞭解這些!給出的回溯演算法模板:
void backtracking(引數) {
if (終止條件) {
存放結果;
return;
}
for (選擇:本層集合中元素(樹中節點孩子的數量就是集合的大小)) {
處理節點;
backtracking(路徑,選擇列表); // 遞迴
回溯,撤銷處理結果
}
}
可以寫出如下回溯演算法C++程式碼:
class Solution {
private:
vector<string> result;// 記錄結果
// startIndex: 搜尋的起始位置,pointNum:新增逗點的數量
void backtracking(string& s, int startIndex, int pointNum) {
if (pointNum == 3) { // 逗點數量為3時,分隔結束
// 判斷第四段子字串是否合法,如果合法就放進result中
if (isValid(s, startIndex, s.size() - 1)) {
result.push_back(s);
}
return;
}
for (int i = startIndex; i < s.size(); i++) {
if (isValid(s, startIndex, i)) { // 判斷 [startIndex,i] 這個區間的子串是否合法
s.insert(s.begin() + i + 1 , '.'); // 在i的後面插入一個逗點
pointNum++;
backtracking(s, i + 2, pointNum); // 插入逗點之後下一個子串的起始位置為i+2
pointNum--; // 回溯
s.erase(s.begin() + i + 1); // 回溯刪掉逗點
} else break; // 不合法,直接結束本層迴圈
}
}
// 判斷字串s在左閉又閉區間[start, end]所組成的數字是否合法
bool isValid(const string& s, int start, int end) {
if (start > end) {
return false;
}
if (s[start] == '0' && start != end) { // 0開頭的數字不合法
return false;
}
int num = 0;
for (int i = start; i <= end; i++) {
if (s[i] > '9' || s[i] < '0') { // 遇到非數字字元不合法
return false;
}
num = num * 10 + (s[i] - '0');
if (num > 255) { // 如果大於255了不合法
return false;
}
}
return true;
}
public:
vector<string> restoreIpAddresses(string s) {
result.clear();
if (s.size() > 12) return result; // 算是剪枝了
backtracking(s, 0, 0);
return result;
}
};
總結
在回溯演算法:分割回文串中我列舉的分割字串的難點,本題都覆蓋了。
而且本題還需要操作字串新增逗號作為分隔符,並驗證區間的合法性。
可以說是回溯演算法:分割回文串的加強版。
在本文的樹形結構圖中,我已經把詳細的分析思路都畫了出來,相信大家看了之後一定會思路清晰不少!
就醬,「程式碼隨想錄」值得推薦給你的朋友們!
一些錄友表示跟不上現在的節奏,想從頭開始打卡學習起來,可以在在公眾號左下方,「演算法彙總」可以找到歷史文章,都是按系列排好順序的,挨個看就可以了,別忘了打卡。
很多錄友都在從頭開始打卡學習,看看前面文章的留言區就知道了,你並不孤單!
我是程式設計師Carl,可以找我組隊刷題,也可以在B站上找到我,本文leetcode刷題攻略已收錄,更多精彩演算法文章盡在公眾號:程式碼隨想錄,關注後就會發現和「程式碼隨想錄」相見恨晚!
如果感覺對你有幫助,不要吝嗇給一個?吧!
相關文章
- 回溯演算法之復原IP地址演算法
- 【LeetCode回溯演算法#06】復原IP地址詳解(練習如何處理邊界條件,判斷IP合法性)LeetCode演算法
- LeetCode-093-復原 IP 地址LeetCode
- 程式碼隨想錄演算法訓練營第24天 | 93.復原IP地址 78.子集 90.子集Ⅱ演算法
- 「leetcode」78. 子集【回溯演算法】詳解!LeetCode演算法
- IP地址詳解
- [LeetCode] 93. Restore IP AddressesLeetCodeREST
- (回溯法)ip地址的合理性
- python leetcode 93. Restore IP AddressesPythonLeetCodeREST
- 【LeetCode回溯演算法#07】子集問題I+II,鞏固解題模板並詳解回溯演算法中的去重問題LeetCode演算法
- LeetCode演算法訓練-回溯總結LeetCode演算法
- 帶你瞭解地址分配DHCP,IP地址管理方式及分配原則
- myloader還原恢復詳解
- 【LeetCode回溯演算法#08】遞增子序列,鞏固回溯演算法中的去重問題LeetCode演算法
- leetcode題解(遞迴和回溯法)LeetCode遞迴
- 回溯演算法演算法
- leetcode.回溯演算法能解決什麼問題?LeetCode演算法
- 回溯演算法 LeetCode 131 分割回文子串演算法LeetCode
- 經典:詳解IP地址盜用常用方法及防範(轉)
- 程式碼隨想錄day24 || 93 復原IP地址,78 子集, 90 子集2
- 【TCP/IP】IP地址分類和特殊IP地址TCP
- 演算法-回溯演算法演算法
- leetcode 130被圍繞的區域 回溯演算法LeetCode演算法
- 演算法題:IP 地址和版本號排序演算法排序
- leetcode:全排列(java回溯)LeetCodeJava
- IP地址
- MAC 地址與IP地址Mac
- [演算法之回溯演算法]演算法
- 特殊IP地址
- LeetCode46 回溯演算法求全排列,這次是真全排列LeetCode演算法
- LeetCode通關:連刷十四題,回溯演算法完全攻略LeetCode演算法
- 帶你瞭解IP地址的計算
- 《網路IP地址管理》IP地址重要性薦
- 常用演算法之回溯法演算法
- 電腦ip地址在哪 電腦ip地址查詢方法
- TCP/IP協議詳解TCP協議
- IP校驗和詳解
- yandexbot ip 地址段