目录
NC78 反转链表
public ListNode ReverseList (ListNode head) {
if(head==null) return head;
ListNode p=head.next,q,tail=head;
tail.next = null;
while(p!=null){
q = p.next;
p.next = tail;
tail = p;
p = q;
}
return tail;
}
NC140 排序
冒泡排序
public int[] MySort (int[] arr) {
for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-i-1;j++){
int temp = arr[j];
if(arr[j]>arr[j+1]){
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
return arr;
}
快排
public int[] MySort (int[] arr) {
quickSort(arr, 0, arr.length - 1);
return arr;
}
public void quickSort(int[] arr, int low, int high) {
if (low >= high) {
return;
}
int base = findBase(arr, low, high);
quickSort(arr, low, base - 1);
quickSort(arr, base + 1, high);
}
public int findBase(int[] arr, int low, int high) {
int base = arr[low];
int start = low;
int end = high;
while (low < high) {
while (low < high && arr[high] > base) {
high--;
}
while (low < high && arr[low] <= base) {
low++;
}
if (low < high) {
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
}
}
arr[start] = arr[high];
arr[high] = base;
return high;
}
NC93 设计LRU缓存结构
初始简单思路:每次放新值、取某个值时候,都把它放队列最后面,这样就能把最久未用的挤到队头,这样可以保证每次超过队列容量时,删除值和放新值时间复杂度都是O(1),因此选用一个双端队列。
进一步细化:每次set一个重复值或是get一个值时,都需要将原来的某个节点给他移动到队尾,我们都知道如果是数组类型的话,这个时间复杂度是O(n-i),如何保证这个时间复杂度也是O(1)呢。因此可以使用循环链表保存前序指针和后继指针,为了方便在队首和队尾插入元素,需要两个指针来指明队首和队尾。如何判断这个值存在不存在,肯定要hashmap了,就需要把每次新产生的节点存到hashmap里面,所以需要自己定义一个类型,hashmap key为key,值为自己定义的这个类型。
还需要一个值表明容量剩余。
那么详细来说思路就是:
set函数:1.首先要判断这个key是否已经存在,如果已经存在,那么将map里的值替换掉,并将该key对应的节点从原来位置删掉,再把该节点插入到队尾。
删除逻辑又分成三种:(1)如果是普通节点,就让node.pre.next=node.next,node.next.pre=node.pre,node.pre=null,node.next=null(2)如果该节点本身就是尾节点,说明不需要移动、不做处理;(3)如果是头结点,那么node.pre就是null,如果采用上面的逻辑就会出现空指针异常,此时只能让head=node.next node.next.pre=null node.next=null。
2.如果不存在,也分为两种情况:(1)队伍满了,需要删除队首,并将该值从hashmap删除,再生成新节点插入队尾,容量不变,仍然为0。(2)队伍还有位置,直接生成新节点插入队尾,容量减1。两种逻辑都不要忘记将新节点的key、value存入到hashmap。
get函数:1.如果存在,找到该节点,将对应节点从原来位置删除,再将该节点插入队尾。(和set部分逻辑一样,可以考虑将删除节点、将某个节点插入到队尾这两段逻辑单独拎出来作为两个函数) 2.如果不存在,返回-1。
import java.util.*;
public class Solution {
//标志队首
Node head;
//标志队尾
Node tail;
class Node {
public int key;
public int value;
public Node pre;
public Node next;
public Node(int key, int value, Node pre, Node next) {
this.value = value;
this.key = key;
this.pre = pre;
this.next = next;
}
}
HashMap<Integer, Node> map = new HashMap<>();
int count = 0;
int maxCap = 0;
public Solution(int capacity) {
maxCap = capacity;
}
public void print(Node n) {
Node p = n;
while (p != null) {
System.out.print("-" + p.value);
p = p.next;
}
System.out.println();
}
public int get(int key) {
//如果存在该元素,将该元素节点移动到队尾,原位置删除
if (map.containsKey(key)) {
Node delNode = map.get(key);
Node preNode = delNode.pre;
Node nextNode = delNode.next;
//在原位置删除该元素
//该元素本来就是队尾,直接返回无需换位置
if (count == 1 || nextNode == null) return delNode.value;
//如果原来是队首,删除逻辑单独处理
if (preNode == null) {
head = nextNode;
nextNode.pre = null;
delNode.next = null;
} else {
preNode.next = delNode.next;
delNode.next.pre = preNode;
}
//将元素加到队尾
tail.next = delNode;
delNode.next = null;
delNode.pre = tail;
tail = delNode;
// System.out.println("----get:-----");
// print(head);
// map.forEach((k, v)->System.out.println("k:" + k + "v:" + v.value));
// System.out.println("输出:" + delNode.value);
return delNode.value;
} else {
//如果不存在返回-1
// System.out.println("----get:-----");
// print(head);
// map.forEach((k, v)->System.out.println("k:" + k + "v:" + v.value));
// System.out.println("输出:-1");
return -1;
}
}
public void set(int key, int value) {
//插入时判断是否已经有该key,有的话需要替换,并将该元素移到队尾
if (map.containsKey(key)) {
Node dulNode = map.get(key);
dulNode.value = value;
map.put(key, dulNode);
//在原位置删除该元素
//该元素本来就是队尾,直接返回无需换位置
if (count == 1 || dulNode.next == null) return;
//如果原来是队首,删除逻辑单独处理
if (dulNode.pre == null) {
head = dulNode.next;
dulNode.next.pre = null;
dulNode.next = null;
} else {
dulNode.pre.next = dulNode.next;
dulNode.next.pre = dulNode.pre;
}
//将元素加到队尾
tail.next = dulNode;
dulNode.next = null;
dulNode.pre = tail;
tail = dulNode;
// System.out.println("----set:-----");
// print(head);
// map.forEach((k, v)->System.out.println("k:" + k + "v:" + v.value));
// System.out.println("输出:null");
} else {
// 插入时判断数量+1是否大于容量
if (maxCap <= 0) return;
if (count + 1 > maxCap) {
//大于容量队首元素出队
map.remove(head.key);
Node tempNode = head.next;
head.next = null;
head = tempNode;
head.pre = null;
count--;
}
//新元素入队尾
Node newNode = new Node(key, value, null, null);
newNode.pre = tail;
if (tail != null) {
tail.next = newNode;
} else {
tail = newNode;
head = newNode;
head.pre = null;
tail.pre = null;
head.next = null;
tail.next = null;
}
tail = newNode;
count++;
map.put(key, newNode);
// System.out.println("----set:-----");
// print(head);
// map.forEach((k, v)->System.out.println("k:" + k + "v:" + v.value));
// System.out.println("输出:null");
}
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution solution = new Solution(capacity);
* int output = solution.get(key);
* solution.set(key,value);
*/
NC102 在二叉树中找到两个节点的最近公共祖先
public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
HashMap<Integer,Integer> hashMap = new HashMap<>();
List<TreeNode> list = new ArrayList<>();
list.add(root);
hashMap.put(root.val,null);
while(!list.isEmpty()){
TreeNode node = list.get(0);
if(node.left!=null){
list.add(node.left);
hashMap.put(node.left.val,node.val);
}
if(node.right!=null){
list.add(node.right);
hashMap.put(node.right.val,node.val);
}
list.remove(0);
}
List<Integer> pathO1 = new ArrayList<>();
pathO1.add(o1);
List<Integer> pathO2 = new ArrayList<>();
pathO2.add(o2);
findPath(hashMap,o1,pathO1);
findPath(hashMap,o2,pathO2);
pathO1.forEach(p->System.out.print(p+" "));
System.out.println();
pathO2.forEach(p->System.out.print(p+" "));
int index1=pathO1.size()-1;
int index2=pathO2.size()-1;
while(index1>=0 && index2>=0 && pathO1.get(index1).equals(pathO2.get(index2))){
index1--;
index2--;
}
if(index1>=0 && index2>=0){
return pathO1.get(index1+1);
}
if(index1<0){
return pathO1.get(0);
}
if(index2<0){
return pathO2.get(0);
}
return 0;
}
public void findPath(HashMap<Integer,Integer> hashMap,Integer o,List<Integer> list){
while(o!=null){
if(hashMap.get(o)!=null)
list.add(hashMap.get(o));
o = hashMap.get(o);
}
}
NC19 连续子数组的最大和
描述
输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。
数据范围:
1<=n<=2×105
−100<=a[i]<=100
要求:时间复杂度为O(n),空间复杂度为 O(n)
进阶:时间复杂度为 O(n),空间复杂度为 O(1)
输入[1,-2,3,10,-4,7,2,-5],返回值:18,经分析可知,输入数组的子数组[3,10,-4,7,2]可以求得最大和为18
思路
用一个动态规划数组,每个位置上的人都有两种选择,加入前面的或者不加入,每个位置上的人努力做到自己这和最大,所以会比对上一位的和加上自己的比自己本身手里的大吗,如果打不过就加入,如果加入还不如自己单干就不加入,全程将最大值记录下来,最后就可以得到最优解。
public int FindGreatestSumOfSubArray (int[] array) {
int[] dp = new int[array.length];
dp[0] = array[0];
int max = dp[0];
for(int i=1;i<array.length;i++){
dp[i] = Math.max(dp[i-1]+array[i],array[i]);
if(dp[i]>max) max = dp[i];
}
return max;
}
NC1 大数加法
这道题注意别超时,不要切割字符串转成int数组,不要用string保存结果再反转,用一个char数组保存。
public String solve (String s, String t) {
if (s.length() <= 0)
return t;
if (t.length() <= 0)
return s;
char[] res = new char[Math.max(s.length(),t.length())];
int high = 0;
int index1 = s.length() - 1;
int index2 = t.length() - 1;
while (index1 >= 0 && index2 >= 0) {
int add = s.charAt(index1) - '0' + t.charAt(index2) - '0' + high;
res[index2] = (char)( add % 10 + '0');
if (add >= 10) high = 1;
else high = 0;
index1--;
index2--;
}
while (index1 >= 0) {
int add = s.charAt(index1) - '0' + high;
res[index1] = (char)( add % 10 + '0');
if (add >= 10) high = 1;
else high = 0;
index1--;
}
while (index2 >= 0) {
int add = t.charAt(index2) - '0' + high;
res[index2] = (char)( add % 10 + '0');
if (add >= 10) high = 1;
else high = 0;
index2--;
}
String result = String.valueOf(res);
if (high == 1) result = "1" + result;
return result;
}
NC41 最长无重复子数组
这题直接暴力法,第一层遍历用i从头到尾,第二层遍历用index从i到尾,如果遍历过程中遇到的map里没有就length加一,加入map,如果遇到重复的,比较length是否大于max,大于则替换,将mao清空,break退出此次。
public int maxLength (int[] arr) {
int max = -1;
int length = 0;
HashMap<Integer, Boolean> hashMap = new HashMap<>();
for (int i = 0; i < arr.length; i++) {
for (int index = i; index < arr.length; index++) {
if (!hashMap.containsKey(arr[index])) {
hashMap.put(arr[index], true);
length++;
if (length > max) max = length;
} else {
hashMap.clear();
length = 0;
break;
}
}
}
return max;
}
NC38 螺旋矩阵
描述
给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。
数据范围:0≤n,m≤10,矩阵中任意元素都满足 0∣val∣≤100
要求:空间复杂度 O(nm) ,时间复杂度 O(nm)
输入 [[1,2,3],[4,5,6],[7,8,9]] 输出 [1,2,3,6,9,8,7,4,5]
思路
递归函数,设置上top下down左left右right四个值作为四个方向的边界,先从左向右输出(j=left,j<right避免和下一个方向的输出重复),再从上往下输出(i=top,i<down),从右往左输出(j=right,j>left),从下往上输出(i=down,i>top),然后将top++,down--,left++,right--传入递归
递归出口为left>right或者top>down
需要处理的一个特殊情况是当left==right或者top==down时说明是单列、单行,此时要将此列、行单独输出后返回。
代码
public ArrayList<Integer> spiralOrder (int[][] matrix) {
int m = matrix.length;
ArrayList<Integer> res = new ArrayList<>();
if(m==0) return res;
int n = matrix[0].length;
circle(matrix, 0, n-1, 0, m-1, res);
return res;
}
public void circle(int[][] matrix, int left, int right, int top, int down,
ArrayList<Integer> res) {
if(left>right || top>down) return;
if(left==right){
//只有一列按列遍历
for(int i=top;i<=down;i++){
res.add(matrix[i][left]);
}
return;
}
if(top==down){
//只有一行按行遍历
for(int j=left;j<=right;j++){
res.add(matrix[down][j]);
}
return;
}
for(int j=left;j<right;j++){
res.add(matrix[top][j]);
}
for(int i=top;i<down;i++){
res.add(matrix[i][right]);
}
for(int j=right;j>left;j--){
res.add(matrix[down][j]);
}
for(int i=down;i>top;i--){
res.add(matrix[i][left]);
}
circle(matrix,left+1,right-1,top+1,down-1,res);
}
NC17 最长回文子串
描述
对于长度为n的一个字符串A(仅包含数字,大小写英文字母),请设计一个高效算法,计算其中最长回文子串的长度。
数据范围: 1≤n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n2)
进阶: 空间复杂度O(n),时间复杂度 O(n)
思路
暴力:i遍历字符串,index遍历从i到字符串尾,对每个子串s.subString(i,index+1),如果是回文串,记录长度,如果大于最大值覆盖。
另外需要一个判断是否为回文串的函数。
时间复杂度为O(N^2)+O(n)
代码
ublic int getLongestPalindrome (String A) {
int maxLength = 1;
int length = 1;
for (int i = 0; i < A.length(); i++) {
int index = i;
length = 1;
for (; index < A.length(); index++) {
if (isPalindrome(A.substring(i, index+1))) {
length = A.substring(i, index+1).length();
if (length > maxLength) {
maxLength = length;
}
}
}
}
return maxLength;
}
public boolean isPalindrome(String A) {
int length = A.length();
Stack<Character> stack = new Stack<>();
for (int i = 0; i < length; i++) {
if (length % 2 != 0) {
if (i < length / 2) {
stack.add(A.charAt(i));
} else if (i > length / 2) {
if (stack.isEmpty() || !stack.pop().equals(A.charAt(i))) {
return false;
}
}
} else {
if (i < length / 2) {
stack.add(A.charAt(i));
} else if (i >= length / 2) {
if (stack.isEmpty() || !stack.pop().equals(A.charAt(i))) {
return false;
}
}
}
}
if (!stack.isEmpty()) return false;
return true;
}
NC54 三数之和
描述
给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。
数据范围:0≤n≤1000,数组中各个元素值满足 ∣val∣≤100
空间复杂度:O(n2),时间复杂度 O(n2)
注意:
- 三元组(a、b、c)中的元素必须按非降序排列。(即a≤b≤c)
- 解集中不能包含重复的三元组。
例如,给定的数组 S = {-10 0 10 20 -10 -40},解集为(-10, -10, 20),(-10, 0, 10)
思路
先将数组排序(时间复杂度O(logn)),用i遍历数组。对每一个i进行循环:i右侧的第一个元素下标赋值给low,最后一个元素下标赋值给high,如果low和high所在位置数字相加等于-arr[i]将这三个元素加入到结果,同时low++,high--来继续寻找,如果小于则low++,如果大于则high--。该循环结束条件应为low不超过右边界(长度-1),high不超过左边界(0),且low<high。
除此之外要考虑去重的问题,可能有两种情况出现重复,
第一种是元素重复被选中:假设输入为[-40,-10,-10,0,10,20]的情况下,如果i遍历到第一个-10,下面去遍历会找到[-10,20][0,10],等到i遍历到第二个-10一样会得到相同的结果,这样就会产生重复。对这种重复的解决办法是,如果arr[i]==arr[i-1]就跳过(所以i需要从1开始)
第二种是进行双指针查找的区间里出现重复值:假设输入为[-10,-10,0,0,10,],i遍历到-10,找到[0,10],[0,10]两个重复。对这种的处理方式是如果arr[low]==arr[low-1]则跳过arr[low],如果arr[high]==arr[high+1]则跳过arr[high]。
为了防止数据越界,此处需要限制high<=arr.length-1,本来也应该限制low>=0,但是low的左边界是i不是0,所以此处应该是限制low>i+1(如果low取i+1,那么也会将arr[i]和arr[i+1]进行比较,arr[i]是我们选中的数,他允许和查找范围内的重复,所以显然不能取此值)
代码
public ArrayList<ArrayList<Integer>> threeSum (int[] num) {
//先排序
Arrays.sort(num);
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
for (int i = 0; i < num.length; i++) {
if (i >= 1 && num[i] == num[i - 1]) {
continue;
}
int low = i + 1;
int high = num.length - 1;
while (low <= num.length - 1 && high >= 1 && low < high) {
if (low > i+1 && num[low] == num[low - 1]) {
low++;
continue;
}
if (high + 1 <= num.length - 1 && num[high] == num[high + 1]) {
high--;
continue;
}
if (num[low] + num[high] == 0 - num[i]) {
ArrayList<Integer> tempList = new ArrayList<Integer>();
tempList.add(num[i]);
tempList.add(num[low]);
tempList.add(num[high]);
result.add(new ArrayList<Integer>(tempList));
low++;
high--;
} else if (num[low] + num[high] < 0 - num[i]) {
low++;
} else {
high--;
}
}
}
return result;
}
NC32 求平方根
描述
实现函数 int sqrt(int x).
计算并返回 x 的平方根(向下取整)
数据范围: 0<=x<231−1
要求:空间复杂度 O(1),时间复杂度 O(logx)
代码
public int sqrt (int x) {
int low = 1;
int high = x;
if (x == 1) return 1;
while (low <= high) {
int mid = (low + high) / 2;
if (x / mid >= mid && x / (mid + 1) <= mid) {
return mid;
} else if (x / mid >= mid) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return 0;
}
NC91 最长上升子序列(三)
描述
给定数组 arr ,设长度为 n ,输出 arr 的最长上升子序列。(如果有多个答案,请输出其中 按数值(注:区别于按单个字符的ASCII码值)进行比较的 字典序最小的那个)
数据范围:0≤n≤200000,0≤arri≤1000000000
要求:空间复杂度 O(n),时间复杂度 O(nlogn)
思路
输入 [2,1,5,3,6,4,8,9,7]
输出 [1,3,4,8,9]
dp[i]表示到arr[i]为止最长的子序列长度,初始化dp为[1,1,1,1,1,1,1,1,1],输入[2]时,最长长度为1,输入[2,1]因为左侧没有比1更大的,所以dp[1]=1,输入[2,1,5],从左往右挨个找比5小的,并且将dp[i]加一,取比5小的里面dp[i]加一最大的。就这样,每个元素都从左往右找所有比自己小的,并且找出其中长度最长的、加一。即dp[i]=Max(dp[j])+1 (j<i,arr[j]<arr[i])
可以得到动态规划数组为:
[1,1,2,2,3,3,4,5,4] 至此可以得出最大上升序列长度,但怎样求解结果序列呢?
可以看出结果其实是[1,1],[2,2],[3,3],[4],[5]里面分别找到原来值最小的(最大长度5已经出现了,它右侧的4就没意义了)
我们考虑两个dp值一样的序列,如果右面的数组值比左面数组值大,那么右面的dp值应该至少为左面的dp值加一才对,显然与现实情况不符,所以我们可以得到假设条件不正确,即右面的数组值应该小于左边的值。因此想找到每个分组里数组值最小的,我们只要得到每组里最右边的一个就可以了。
至此得到时间复杂度为O(n^2)的思路。代码如下:
public int[] LIS (int[] arr) {
int[] dp = new int[arr.length];
if (arr.length == 0) return dp;
int maxLength = 0;
int max=0;
Arrays.fill(dp,1);
for (int i = 0; i < arr.length; i++) {
for(int j=0;j<i;j++){
if(arr[j]<arr[i]){
dp[i] = Math.max(dp[j]+1,dp[i]);
}
}
maxLength = Math.max(maxLength,dp[i]);
}
int[] res = new int[maxLength];
int length = maxLength;
for(int i=arr.length-1;i>=0 && length>0;i--){
if(dp[i]==length){
res[length-1] = arr[i];
length--;
}
}
return res;
}
时间复杂度要求为O(nlog n),考虑用二分进行优化。
上面算法耗时多的就在于对每个a[i]都需要遍历i次,如果能够将当前最大长度和该长度对应的最小的数字记录下来,那a[i]就只需要和这个数比较就能知道dp[i]以及a[i]需不需要放到结果集里了。
所以考虑维护一个单调增长的序列tail。
输入 [2,1,5,3,6,4,8,9,7]
tail[] input[] dp[]
tail[2] input[2] dp[1]
tail[1] input[2,1] dp[1,1]
tail[1,5] input[2,1,5] dp[1,1,2]
tail[1,3] input[2,1,5,3] dp[1,1,2,2]
tail[1,3] input[2,1,5,3,6] dp[1,1,2,2,3] ...
可以看出规律为每次新增的数如果大于队尾,就直接加入到tail,tail长度加一,并且dp[i]=tail长度,如果每次新增的数字小于队尾,那就需要把从右到左第一个大于他的数取代掉,这个数的位置temp表示当前arrp[i]对应的递增序列结尾处,所以dp[i]=temp+1。这个寻找取代数的过程可以用二分,整体复杂度就降低为O(nlogn)了。
public int[] LIS (int[] arr) {
int[] dp = new int[arr.length];
if (arr.length == 0) return dp;
int maxLength = 0;
int max = 0;
Arrays.fill(dp, 1);
int[] tail = new int[arr.length];
//index表示tail内的元素个数
int index = 0;
for (int i = 0; i < arr.length; i++) {
//tail里没有元素或者当前值大于队尾,直接加入
if (i == 0 || arr[i] > tail[index - 1]) {
tail[index] = arr[i];
index++;
dp[i] = index;
} else {
//替换,tail数组长度不变,dp数组长度加一
int temp = findPlace(tail, arr[i], index);
tail[temp] = arr[i];
dp[i] = temp + 1;
}
}
int[] res = new int[index];
for (int i =arr.length- 1; i >= 0 && index > 0; i--) {
if (dp[i] == index) {
res[--index] = arr[i];
}
}
return res;
}
public int findPlace(int[] tail, int key, int index) {
int low = 0;
int high = index - 1;
while (low < high) {
int mid = (low + high) / 2;
if (tail[mid] < key) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
NC48 在旋转过的有序数组中寻找目标值
描述
有一个长度为 n 的按严格升序排列的整数数组 nums ,在实行 search 函数之前,在某个下标 k 上进行旋转,使数组变为[nums[k],nums[k+1],.....,nums[nums.length-1],nums[0],nums[1],.......,nums[k-1]]。
给定旋转后的数组 nums 和一个整型 target ,请你查找 target 是否存在于 nums 数组中并返回其下标(从0开始计数),如果不存在请返回-1。
数据范围:0≤n≤10000,0≤target≤100000
要求:空间复杂度 O(1) ,时间复杂度 O(n)
比如,数组[0,2,4,6,8,10]在下标3处旋转之后变为[6,8,10,0,2,4], 当给定target为10时,10的下标是2,target为3时,nums数组中不存在3,所以返回-1
思路
数组[0,2,4,6,8,10]在下标3处旋转之后变为[6,8,10,0,2,4] 可以看出旋转后数组的特点是k左侧为递增序列,k右侧也为递增序列,取mid为中间值。
如果num[mid]大于等于num[left]说明mid左侧为递增序列,mid可能为两个递增序列的分界线,也可能属于左侧递增序列的一部分。如中间值为8,左侧序列为6,8 右侧序列为10,0,2,4。如果num[mid]小于num[right]说明mid右侧为递增序列,mid可能为两个递增序列的分界线,也可能属于右侧递增序列的一部分,如中间值为2,左侧序列为6,8,10,0,右侧序列为4。
1.如果左侧为递增序列
(1)target>=num[mid],说明target不在mid左边,下一次取值区间为(mid+1,right)。
(2)如果target<num[mid],说明target可能在左边序列mid左侧那部分,也可能在右侧序列中。所以需要再判断target是否大于num[left],如果大于那么target就属于左侧序列的左边部分,下一次取值空间为(left,mid-1),如果小于,说明target应该在右侧序列中,下一次取值为(mid+1,right)。
2.如果右侧为递增序列
(1)target<=num[mid],说明target不在mid右边,下一次取值为(left,mid-1)。
(2)如果target>num[mid],说明可能在mid右侧也可能在左侧序列。判断target是否大于num[right],如果大于说明在左侧序列,下次取(left,mid-1),如果小于说明在mid右侧,取(mid+1,right)
代码
public int search (int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
//如果mid左侧数组有序
if (nums[mid] >= nums[left]) {
if (target > nums[mid]) {
left = mid + 1;
} else {
if (target >= nums[left]) {
right = mid;
} else {
left = mid + 1;
}
}
}
//如果mid右侧数组有序
else if (nums[mid] < nums[right]) {
if (target < nums[mid]) {
right = mid;
} else {
if (target > nums[right]) {
right = mid;
} else {
left = mid + 1;
}
}
}
}
return -1;
}
BM74 数字字符串转化成IP地址
描述
现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。
例如:
给出的字符串为"25525522135",
返回["255.255.22.135", "255.255.221.35"]. (顺序没有关系)
数据范围:字符串长度 0≤n≤12
要求:空间复杂度 O(n!),时间复杂度 O(n!)
注意:ip地址是由四段数字组成的数字序列,格式如 "x.x.x.x",其中 x 的范围应当是 [0,255]。
思路
2 5 5 2 5 5 2 2 1 3 5
0 1 2 3 4 5 6 7 8 9 10(长度为n=11)
将字符串变为ip地址,需要插入三个.。
用i表示第一个点插入的下标,那么i应该大于等于1(保证第第一段数字长度至少为1)且小于等于3(保证第一段数字长度至多为3),且i应该小于n-2(保证后面至少有三个数,有两个空留给剩下两个点)
用j表示第二个点插入的下标,那么i+1<=j<=i+3 && j<n-1
用k表示第三个点插入的下标,那么j+1<=k<=j+3&&k<n
以上为三层循环,在循环中判断:
字符串1 = s.subString(0,i)
字符串2 = s.subString(i,j)
字符串3 = s.subString(j)
如果最后一段字符串长度大于3 退出此次循环
这三个字符串转换为数字是否小于255 如果大于continue退出此次循环
如果三个字符串任意一个首数字为0且长度不为1 也不符合 同样退出此次循环
符合条件的 加入到结果集
代码
import java.util.*;
public class Solution {
public ArrayList<String> restoreIpAddresses (String s) {
ArrayList<String> res = new ArrayList<String>();
int n = s.length();
//遍历IP的点可能的位置(第一个点)
for(int i = 1; i < 4 && i < n - 2; i++){
//第二个点的位置
for(int j = i + 1; j < i + 4 && j < n - 1; j++){
//第三个点的位置
for(int k = j + 1; k < j + 4 && k < n; k++){
//最后一段剩余数字不能超过3
if(n - k >= 4)
continue;
//从点的位置分段截取
String a = s.substring(0, i);
String b = s.substring(i, j);
String c = s.substring(j, k);
String d = s.substring(k);
//IP每个数字不大于255
if(Integer.parseInt(a) > 255 || Integer.parseInt(b) > 255 || Integer.parseInt(c) > 255 || Integer.parseInt(d) > 255)
continue;
//排除前导0的情况
if((a.length() != 1 && a.charAt(0) == '0') || (b.length() != 1 && b.charAt(0) == '0') || (c.length() != 1 && c.charAt(0) == '0') || (d.length() != 1 && d.charAt(0) == '0'))
continue;
//组装IP地址
String temp = a + "." + b + "." + c + "." + d;
res.add(temp);
}
}
}
return res;
}
}