目录
17.longest-consecutive-sequence
24.median-of-two-sorted-arrays
13.single-number
题目:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
分析:利用异或运算的一个性质:任何一个数字异或它自己都等于0。也就是说,如果我们从头到尾依次异或数组中的每个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些成对出现两次的数字全部在异或过程中抵消了。
public int singleNumber(int[] nums) {
int res = nums[0];
for(int i = 1;i < nums.length;i++)
res ^= nums[i];
return res;
}
14.single-number-ii
题目:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
分析:见剑指offer面试题56II https://blog.csdn.net/Nibaby9/article/details/104126765
15.remove-element
题目:给定一个数组和一个值,使用就地算法将数组中所有等于这个值的元素删除,并返回新数组的长度。元素的顺序可以更改。你不用去关心大于当前数组长度的空间里面存储的值
分析:用快排思想,当遇到不等于目标值,将最后一个值往前移即可。
public int removeElement(int[] A, int elem) {
int low = 0,high = A.length - 1;
while(low <= high){
while(low <= high && A[low] != elem)
low++;
if(low <= high){
A[low] = A[high];
high--;
}
}
return high + 1;
}
16.sort-colors
问题:现在有一个包含n个物体的数组,其中物体颜色为颜色为红色、白色或蓝色,请对这个数组进行排序,让相同颜色的物体相邻,颜色的顺序为红色,白色,蓝色。我们用0,1,2分别代表颜色红,白,蓝。注意:本题要求你不能使用排序库函数。扩展:一个非常直接的解法是两步的计数排序的算法,首先:遍历一遍数组,记录0,1,2的数量,然后重写这个数组,先将0写入,再将1写入,再将2写入,你能给出一个只用一步,并且能在常数级空间复杂度解决这个问题的算法吗?
分析:设置三个变量,一个变量记录0的后一个位置,一个变量记录2前一个的位置,一个变量用来遍历当前数组。
public void sortColors(int[] A) {
int low = 0,index = 0,high = A.length - 1;
while(index <= high){
if(A[index] == 0){
A[low] = 0;
low++;index++;
}
else if(A[index] == 2){
A[index] = A[high];
A[high] = 2;
high--;
}
else
index++;
}
while(low <= high)
A[low++] = 1;
}
17.longest-consecutive-sequence
题目:给定一个无序的整数类型数组,求最长的连续元素序列的长度。例如:给出的数组为[100, 4, 200, 1, 3, 2],最长的连续元素序列为[1, 2, 3, 4]. 返回这个序列的长度:4。你需要给出时间复杂度在O(n)之内的算法
分析:先初始化一个HashMap,key为数组中的每个元素,value表示是否访问过;然后再遍历这个数组,看该元素是否访问过,如果没有则统计其相邻元素是否存在,每访问一个元素都将其value设置为true。
public int longestConsecutive(int[] num) {
if(num == null || num.length == 0)
return 0;
int max = 1;
HashMap<Integer,Boolean> map = new HashMap();
for(int i = 0;i < num.length;i++)
map.put(num[i],false);
for(int i = 0;i < num.length;i++){
int count = 1;
if(! map.get(num[i])){
int key = num[i];
map.put(key,true);
while(map.containsKey(--key) && !map.get(key)){
count++;
map.put(key,true);
}
key = num[i];
while(map.containsKey(++key) && !map.get(key)){
count++;
map.put(key,true);
}
if(count > max)
max = count;
}
}
return max;
}
18.spiral-matrix
题目:给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。例如,给出以下矩阵:[↵ [ 1, 2, 3 ],↵ [ 4, 5, 6 ],↵ [ 7, 8, 9 ]↵],你应该返回[1,2,3,6,9,8,7,4,5]。
分析:见剑指offer面试题29 https://blog.csdn.net/Nibaby9/article/details/104126765
19.spiral-matrix-ii
题目:给定一个整数n,将数字1到n^2n2按螺旋的顺序填入n×n的矩阵。例如:给出的n=3,你应该返回如下矩阵:[↵ [ 1, 2, 3 ],↵ [ 8, 9, 4 ],↵ [ 7, 6, 5 ]↵]
分析:同上题思路一样,分别用四个数记录开始行、结束行、开始列、结束列即可。
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n];
int row = 0, row_end = n - 1, col = 0, col_end = n - 1;
int count = 1, sum = n * n;
while (count <= sum) {
for (int i = col; i <= col_end; i++, count++)
result[row][i] = count;
row++;
for (int i = row; i <= row_end; i++, count++)
result[i][col_end] = count;
col_end--;
for (int i = col_end; i >= col; i--, count++)
result[row_end][i] = count;
row_end--;
for (int i = row_end; i >= row; i--, count++)
result[i][col] = count;
col++;
}
return result;
}
20.rotate-image
题目:给出一个用二维矩阵表示的图像,返回该图像顺时针旋转90度的结果。扩展:你能使用原地算法解决这个问题么?
分析:一个点顺时针旋转90度,就是该点关于y = x对称,再关于x轴对称。所以只要将图像做两次翻转,先沿右上-左下的对角线翻转,再沿水平中线上下翻转。注:点(i,j)沿对角线翻转的对应点为(n-1-j,n-1-i).
public void rotate(int[][] matrix) {
int n = matrix.length;
for(int i = 0;i < n;i++){
for(int j = 0;j < n - i;j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[n-1-j][n-1-i];
matrix[n-1-j][n-1-i] = temp;
}
}
for(int i = 0;i < n / 2;i++){
for(int j = 0;j < n;j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[n-1-i][j];
matrix[n-1-i][j] = temp;
}
}
}
21.merge-intervals
题目:给出一组区间,请合并所有重叠的区间。例如,给出[1,3],[2,6],[8,10],[15,18],返回[1,6],[8,10],[15,18].
分析:先利用list中的比较器对区间的x值进行排序,再遍历每个区间,查看该区间与上一区间是否重叠,如果重叠则合并,并把上个区间设为null,最后再移除所有的null值即可。
public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
Collections.sort(intervals, new Comparator<Interval>() {//intervals中按start从小到大排序
@Override
public int compare(Interval o1, Interval o2) {
return o1.start - o2.start;
}
});
for(int i = 1;i < intervals.size();i++){
int x1 = intervals.get(i - 1).start;
int y1 = intervals.get(i - 1).end;
int x2 = intervals.get(i).start;
int y2 = intervals.get(i).end;
if(x2 <= y1){
intervals.set(i,new Interval(x1,Math.max(y1,y2)));
intervals.set(i-1,null);
}
}
while (intervals.remove(null)) ;//删除list中所有的null值
return intervals;
}
22.insert-interval
题目:给定一组不重叠的时间区间,在时间区间中插入一个新的时间区间(如果有重叠的话就合并区间)。这些时间区间初始是根据它们的开始时间排序的。示例1:给定时间区间[1,3],[6,9],在这两个时间区间中插入时间区间[2,5],并将它与原有的时间区间合并,变成[1,5],[6,9].示例2:给定时间区间[1,2],[3,5],[6,7],[8,10],[12,16],在这些时间区间中插入时间区间[4,9],并将它与原有的时间区间合并,变成[1,2],[3,10],[12,16].这是因为时间区间[4,9]覆盖了时间区间[3,5],[6,7],[8,10].
分析:直接将时间区间加入list中,利用上题代码即可。或者将该区间插入,再依次两两合并即可,注意插入位置为第一个位置的情况。
public ArrayList<Interval> insert(ArrayList<Interval> intervals, Interval newInterval) {
if(intervals.size() == 0){
intervals.add(newInterval);
return intervals;
}
int i = 0;
while(i < intervals.size() && intervals.get(i).start <= newInterval.start)
i++;
intervals.add(i,newInterval);
if(i == 0)//插在第一个位置
i = 1;
for(;i < intervals.size();i++){
int x1 = intervals.get(i-1).start;
int y1 = intervals.get(i-1).end;
int x2 = intervals.get(i).start;
int y2 = intervals.get(i).end;
if(x2 <= y1){
intervals.set(i,new Interval(x1,Math.max(y1,y2)));
intervals.set(i - 1,null);
}
}
while(intervals.remove(null));
return intervals;
}
23.container-with-most-water
题目:给定n个非负整数a1,a2,…,an,其中每个数字表示坐标(i, ai)处的一个点。以(i,ai)和(i,0)(i=1,2,3...n)为端点画出n条直线。你可以从中选择两条线与x轴一起构成一个容器,最大的容器能装多少水?注意:你不能倾斜容器。例如:输入 [1,8,6,2,5,4,8,3,7],输出: 49
分析:从两端开始向中间逼近找全局最优,每次移动较短边(移动较长边可能会导致下一个矩形因为短边限制取不到最优)。
public int maxArea(int[] height) {
int max = 0;
int left = 0,right = height.length - 1;
while(left < right){
int area = (right - left) * Math.min(height[left],height[right]);
if(area > max)
max = area;
//每次移动最短边
if(height[left] < height[right])
left++;
else
right--;
}
return max;
}
24.median-of-two-sorted-arrays
题目:有两个大小分别为m和n的有序数组A和B。请找出这两个数组的中位数。你需要给出时间复杂度在O(log (m+n))以内的算法。
分析:将原问题转变为寻找第k小的数。假设数组A和B的元素个数都大于k/2,比较A[k/2-1]和B[k/2-1]两个元素,根据不同情况递归求解即可。注意边界,开始查找索引大于数组长度以及k=1的情况。
public double findMedianSortedArrays(int A[], int B[]) {
int len = A.length + B.length;
if(len % 2 == 1)
return findKth(A,0,B,0,len / 2 + 1);
else
return (findKth(A,0,B,0,len / 2) + findKth(A,0,B,0,len / 2 + 1)) / 2;
}
private double findKth(int[] a, int as, int[] b, int bs, int k) {
if (as >= a.length)
return b[bs + k - 1];
if (bs >= b.length)
return a[as + k - 1];
if(k == 1)
return Math.min(a[as],b[bs]);
int aMin = Integer.MAX_VALUE, bMin = Integer.MAX_VALUE;
if(as + k / 2 - 1 < a.length)
aMin = a[as + k / 2 - 1];
if(bs + k / 2 - 1 < b.length)
bMin = b[bs + k / 2 - 1];
if(aMin < bMin)
return findKth(a,as + k/2,b,bs,k - k/2);
else
return findKth(a,as,b,bs + k/2,k - k/2);
}
25.search-a-2d-matrix
题目:请写出一个高效的在m*n矩阵中判断目标值是否存在的算法,矩阵具有如下特征:每一行的数字都从左到右排序,每一行的第一个数字都比上一行最后一个数字大。例如:对于下面的矩阵:[↵ [1, 3, 5, 7],↵ [10, 11, 16, 20],↵ [23, 30, 34, 50]↵],要搜索的目标值为3,返回true。
分析:见剑指offer面试题4 https://blog.csdn.net/Nibaby9/article/details/104126765
26.plus-one
题目:给出用数字数组表示的一个非负整数,请对该整数加1。
分析:递归即可。特别注意99+1这种最高位需要进位的情况。
public int[] plusOne(int[] digits) {
if(digits.length == 0)
return digits;
return plusOne(digits,digits.length - 1);
}
private int[] plusOne(int[] digits, int index) {
if(index < 0){//最高位进位!!!
int[] a = new int[digits.length + 1];
a[0] = 1;
for(int i = 0;i < digits.length;i++)
a[i + 1] = digits[i];
return a;
}
digits[index]++;
if(digits[index] <= 9)
return digits;
digits[index] = 0;
return plusOne(digits,index - 1);
}
27.max-points-on-a-line
题目:对于给定的n个位于同一二维平面上的点,求最多能有多少个点位于同一直线上
分析:两个点构成一条直线,所以对于n个点可以构成n-1条直线(需考虑两个点重合的情况),只要判断有多少个点在这些直线上即可。而为了避免除数为0而出错的情况,我们用 dy * (x1-x2)== dx * (y1-y2)来判断一个点是否在一条直线上。
public int maxPoints(int[][] points) {
int len = points.length;
if(len < 3)
return len;
//先对点进行排序,将横坐标纵坐标差值较小的点排在前面
Arrays.sort(points, new Comparator<int[]>(){
public int compare(int[] arr1, int[] arr2){
return Math.abs(arr1[0] - arr1[1]) - Math.abs(arr2[0] - arr2[1]);
}
});
int sum = 0;
for(int i = 1;i < len;i++){
int count = 0;
int dx = points[i-1][0] - points[i][0];
int dy = points[i-1][1] - points[i][1];
if(dx==0 && dy==0){//重合点
for(int j = 0;j < len;j++){
if(points[j][0] == points[i][0] && points[j][1] == points[i][1])
count++;
}
}
else {
for(int j = 0;j < len;j++){//long型防止溢出
long detay = points[j][1] - points[i][1];
long detax = points[j][0] - points[i][0];
if (dx * detay == dy * detax)
count++;
}
}
sum = Math.max(count,sum);
}
return sum;
}