前言
初始內容:常見算法題
博客地址:芒果橙的個人博客 【http://mangocheng.com】
一、字符串
1. KMP算法
- 概念:對字符串進行切割分組(前綴、後綴),按順序匹配時,利用分組子串提高匹配效率
- 作用:解決字符串查找的問題
- 時間複雜度O(m+n) 空間複雜度O(m)
- 延伸
- 暴力匹配算法:每次匹配失敗,都重新回溯(匹配不到,索引回到上一次匹配到的位置,再+1繼續從第一個開始匹配)
2. 替換空格
題目:將給定的字符串中的空格全部替換爲233
- 常規方法:遍歷,查找到空格的字符串索引位置,再進行添加
- API: string.replaceAll("\s",“233”);
3. 最長公共前綴
題目:查找給定字符串數組中的最長公共前綴
- 特別的技巧思路:先排序
// 不使用排序則需要嵌套循環
private static void getPrefix(String[] ss) {
String str = new String();
A:
for (int i = 0; i < ss[0].length() ; i++) {
String prefix = ss[0].substring(0,i+1);
B:
for (int j = 1; j < ss.length ; j++) {
if(ss[j] .startsWith(prefix)){
// 全部比中
if(j == ss.length-1){
str = prefix;
}
}else {
System.out.println(str);
break A;
}
}
}
}
4. 迴文串
題目:給定字符串(區分大小寫),構造出最長的迴文串(正讀、反讀一致),返回長度
private static int getLengthOfHw(String s) {
int len = 0;
// 1.奇數、偶數字符數量
int oddCount = 0;
int evenCount = 0;
// 2.計算各個字符的特徵
String temp = new String(s);
while (temp.length() > 0) {
int beforeLength = temp.length();
System.out.println("[Param:beforeLength]" + beforeLength);
String single = temp.charAt(0) + "";
temp = temp.replaceAll(single, "");
int afterLength = temp.length();
System.out.println("[Param:afterLength]" + afterLength);
int count = beforeLength - afterLength;
if ((count & 1) != 1) {
// 2.1偶數累加
evenCount = evenCount + count;
} else if ((count & 1) == 1) {
// 2.2奇數保存最長的
if (oddCount < count) {
oddCount = count;
}
}
}
// 3.計算總數:奇數+偶數
len = oddCount + evenCount;
return len;
}
題目:驗證迴文串,忽略空格,大小寫
- 技巧:判斷字符是否爲子母或數字 Character.isLetterOrDigit()
private static boolean vertifyHw(String s, int frontIndex, int endIndex){
boolean flag = false;
// 1.判斷完畢
if (frontIndex >= endIndex) {
flag = true;
}
System.out.println("[Method:vertifyHw][Params:frontIndex/endIndex]" + frontIndex + "-" + endIndex);
// 2.逐個判斷
char front = s.charAt(frontIndex++);
char end = s.charAt(endIndex--);
System.out.println("[Method:vertifyHw][Params:front/end]" + front + "-" + end);
// 3.判斷
if(Objects.equals(front,end)){
flag = true;
}
// 4.遞歸
if(flag){
return vertifyHw(s,frontIndex,endIndex);
}
return flag;
}
private static boolean vertifyHw2(String s, int frontIndex, int endIndex) {
// 1.判斷完畢
if (frontIndex >= endIndex) {
return true;
}
System.out.println("[Method:vertifyHw][Params:frontIndex/endIndex]" + frontIndex + "-" + endIndex);
// 2.逐個判斷
char front = s.charAt(frontIndex++);
char end = s.charAt(endIndex--);
System.out.println("[Method:vertifyHw][Params:front/end]" + front + "-" + end);
// 3.判斷
if (!Objects.equals(front, end)) {
return false;
}
// 4.遞歸
return vertifyHw2(s, frontIndex, endIndex);
}
題目:給定字符串,找出其中最長的迴文子串
- 技巧:中心擴展法
// 獲取給定字符串中,最長的迴文子串
private static String getLongestHw(String s) {
String longestHw = "";
// 2.遍歷所有迴文子串
for (int i = 1; i < s.length() - 1; i++) {
String hw = getLongestHw(s, i);
// 3.保存最長
if(longestHw.length()<hw.length()){
longestHw = hw;
}
}
return longestHw;
}
// 根據字符串和索引,獲取以該索引爲中心的最大回文串
private static String getLongestHw(String s, int midIndex) {
int len = s.length();
if (midIndex == 0 || midIndex == len) {
return "";
}
// 1.左右延伸
int endIndex = midIndex + 1;
int startIndex = midIndex - 1;
String hw = "";
// 2.判斷
while (startIndex >= 0 && endIndex <= len - 1) {
System.out.println("[Method:even][Datas:endIndex/startIndex]" + endIndex + "/" + startIndex);
// 2.1字符
char start = s.charAt(startIndex);
char end = s.charAt(endIndex);
// 2.2迴文
if (Objects.equals(start, end)) {
hw = s.substring(startIndex, endIndex + 1);
System.out.println("[Description:迴文][Data:halfEndHw]" + hw);
} else {
}
++endIndex;
--startIndex;
}
return hw;
}
// LeetCode答案
static class Solution {
private int index, len;
// 遍歷獲取最長迴文子串
public String longestPalindrome(String s) {
if (s.length() < 2)
return s;
for (int i = 0; i < s.length() - 1; i++) {
PalindromeHelper(s, i, i); // 以i爲中心,向左右兩邊延伸-奇數
PalindromeHelper(s, i, i + 1); // 以i,i+1兩個爲中心,向左右延伸-偶數
}
return s.substring(index, index + len);
}
// 計算起始索引和長度,得出最長迴文串: 以l、r爲中心,向左右兩邊延伸。注:若l = r,則以l爲中心,
public void PalindromeHelper(String s, int l, int r) {
while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
l--;
r++;
}
//替換最長的迴文
if (len < r - l - 1) {
index = l + 1;
len = r - l - 1;
}
}
}
5. 括號匹配深度
題目:給定一個合法的括號序列,獲取其深度
- 思路:從第一個字符往後遍歷,遇到 ( 則count++,否則 count–;每次循環保存max值,max 保存每次循環中max和count的最大值
// 獲取括號的深度
private static int getDepthOfBracket(String s) {
// 1.記錄每輪的最大值
int max = 0;
int count = 0;
// 2.遍歷所有字符
for (int i = 0; i < s.length(); i++) {
char a = s.charAt(i);
if (Objects.equals(a, '(')) {
++count;
} else {
--count;
}
System.out.println("[Method:getDepthOfBracket-for][Datas:count/max]" + count + "/" + max);
max = Math.max(max, count);
}
return max;
}
6. 字符串轉化爲整數
題目:將字符串轉化爲整數,不合法的則返回0
- 思路:類型轉化,將每個字符通過加法計算出來。
- 注意點:char轉化int,是通過ascii碼。
private static int stringToInt(String s) {
int value = 0;
// 1.判斷是否含有正負字符
// 2.循環計算
for (int i = 0; i < s.length(); i++) {
char temp = s.charAt(i);
if (Character.isDigit(temp)) {
// 2.1 ASCII碼的加減,減去'0' 即可獲取到與int相等的值
int g = temp - '0';
value = value * 10 + g;
System.out.println("[Method:stringToInt-for][Datas:g/value]" + g + "/" + value);
} else {
return 0;
}
}
return value;
}
二、簡單排序算法
1.冒泡排序
它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因爲越小的元素會經由交換慢慢“浮”到數列的頂端。【維基百科】
- 思路:第一個字符開始,從頭到尾依次相鄰比較,大的放後面;循環此步驟
- 空間複雜度:O(1)
- 時間複雜度
- 最好:O(1)
- 最壞:O(n^2)
- 平均:O(n^2)
// 1.冒泡排序,從小到大
public static int[] bubbleSort(int[] arr){
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (j + 1 == arr.length) {
break;
}
// 位移運算:一個數對另一個數位異或兩次,該數不變
if (arr[j] > arr[j + 1]) {
arr[j] = arr[j] ^ arr[j + 1];
arr[j + 1] = arr[j] ^ arr[j + 1];
arr[j] = arr[j] ^ arr[j + 1];
}
}
}
return arr;
}
// 2.冒泡排序,從小到大
public static int[] bubbleSort2(int[] arr) {
// 1.總共幾次循環
for (int i = arr.length - 1; i > 0 ; i--) {
// 2.一次循環的比較
for (int j = 0; j < i; j++) {
// 2.1位移運算:一個數對另一個數位異或兩次,該數不變
if (arr[j] > arr[j + 1]) {
arr[j] = arr[j] ^ arr[j + 1];
arr[j + 1] = arr[j] ^ arr[j + 1];
arr[j] = arr[j] ^ arr[j + 1];
}
}
}
return arr;
}
2.選擇排序
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。【維基百科】
- 思路:從第一個字符開始往後比較,知道找到最值,交換位置;循環此步驟
- 空間複雜度:O(1)
- 時間複雜度
- 最好:O(1)
- 最壞:O(n^2)
- 平均:O(n^2)
// 選擇排序,從小到大
public static int[] selectionSort(int[] arr) {
// 1.總共輪次
for (int i = 0; i < arr.length; i++) {
int minIndex = i;
// 2.每輪比較次數
for (int j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j]) {
minIndex = j;
}
}
// 3.交換:自己本身是最值,則不用
if (i != minIndex) {
arr[i] = arr[i] ^ arr[minIndex];
arr[minIndex] = arr[i] ^ arr[minIndex];
arr[i] = arr[i] ^ arr[minIndex];
}
}
return arr;
}
3.插入排序
工作原理是通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。【維基百科】
- 思路:從第一個字符開始,每次往後一個字符與前面(已經排好序)進行比較
- 空間複雜度:O(1)
- 時間複雜度
- 最好:O(1)
- 最壞:O(n^2)
- 平均:O(n^2)
// 插入排序
public static int[] insertionSort(int[] arr){
// 從左往右
for (int i = 0; i < arr.length; i++) {
// 新一個字符,逐個與前面字符的比較:從有望走
for (int j = i - 1; j >= 0; j--) {
if (arr[j] > arr[j+1]) {
arr[j] = arr[j] ^ arr[j + 1];
arr[j + 1] = arr[j] ^ arr[j + 1];
arr[j] = arr[j] ^ arr[j + 1];
}
}
}
return arr;
}
4.希爾排序
也稱遞減增量排序算法,是插入排序的一種更高效的改進版本。希爾排序是非穩定排序算法。通過將比較的全部元素分爲幾個區域來提升插入排序的性能。【維基百科】
-
思路:先進行分組排序,再對每一組進行插入排序,每完成一次排序,都對分組數進行縮小
-
空間複雜度:O(1)
-
時間複雜度
- 最好:O(1)
- 最壞:O(n^2)
// 希爾排序
public static int[] shellSort(int[] arr) {
// 1.獲取小組數
int count = arr.length / 2;
int current;
while (count > 0) {
for (int i = count; i < arr.length; i++) {
current = arr[i];
int j = i - count;
// 分組裏的數據進行插入排序
while (j >= 0 && current < arr[j]) {
arr[j + count] = arr[j];
j -= count;
}
arr[j + count] = current;
}
count /= 2;
}
return arr;
}
5.快速排序
又稱分區交換排序),簡稱快排,是對冒泡排序的一種改進。通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。【百度百科】
- 思路:找到基準數字,每次分組(子序列)比較,遞歸排序
- 複雜度
- 空間複雜度:O(log2n))
- 時間複雜度:O(nlog2n)
// 快速排序
public static int[] quickSort(int[] arr){
quickSort(arr, 0, arr.length - 1);
return arr;
}
// 快速排序具體方法:遞歸
public static int[] quickSort(int[] arr,int left,int right){
// 1.結束
if (left > right) {
return;
}
// 基準數
int i = left;
int j = right;
int base = arr[left];
// 2.比較
while (i != j) {
// 1.右側編號查找數字:比基準數小的索引
while (j > i && arr[j] >= base) {
j--;
}
// 2.左側編號查找數字:比基準數大的索引
while (j > i && arr[i] <= base) {
i++;
}
// 3.數字交換:
if (j > i) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
}
// 3.基準數字和left 位置數字交換
arr[left] = arr[i];
arr[i] = base;
// 4.左、右側數字再次排序
quickSort(arr, left, i - 1);
quickSort(arr, i + 1, right);
}