一、数组中的逆序对
(1)题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
(2)题目分析
首先想的是将数组排序,在排序的过程中,比较前后数字的大小,然后统计逆序的个数。而在排序算法中,归并排序可以很好的解决这个问题。在归并排序的过程中,将两个有序的子数组合并时,如果数组A中的某一个数字大于数组B中的某个数字,那么数组A中该数字后面的所有数字均大于数组B中的这个数字。将所有的这种情况加起来,便得到了这个数组中的逆序对的总数。
(3)代码
package swordOffer;
/**
* @author chengzhengda
* @version 1.0
* @date 2020-03-20 13:34
* @desc 数组中的逆序对
*/
public class t1 {
int sum = 0;
public int reversePairs(int[] nums) {
return mergeSort(nums, 0, nums.length - 1);
}
/**
* 归并排序
*
* @param arr
* @param begin
* @param end
*/
public int mergeSort(int[] arr, int begin, int end) {
if (begin >= end) {
return 0;
}
int mid = (begin + end) / 2;
mergeSort(arr, begin, mid);
mergeSort(arr, mid + 1, end);
return merge(arr, begin, mid, end);
}
/**
* 合并2个有序数组
*
* @param arr
* @param begin
* @param mid
* @param end
*/
public int merge(int[] arr, int begin, int mid, int end) {
int temp[] = new int[end - begin + 1];
for (int index = 0, i = begin, j = mid + 1; index < temp.length; index++) {
if (i > mid) {
temp[index] = arr[j++];
} else if (j > end) {
temp[index] = arr[i++];
} else if (arr[i] <= arr[j]) {
temp[index] = arr[i++];
} else {
temp[index] = arr[j++];
sum += mid - i + 1;
}
}
for (int j = 0; j < temp.length; j++) {
arr[begin + j] = temp[j];
}
return sum;
}
public static void main(String[] args) {
int arr[] = {1, 6, 5, 8, 2, 3, 9, 4, 7};
t1 mm = new t1();
System.out.println(mm.reversePairs(arr));
}
}
二、两个链表的第一个公共节点
(1)题目描述
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
(2)题目分析
对于两个链表,如果长度一样,那么通过遍历这两个链表,依次比较每一个节点是否相同即可,而如果长度不一样,则难以进行比较。那么只要想办法让这两个链表长度相同,便较容易求出公共节点,而A+B的长度一定等于B+A长度,因此,当遍历的时候,如果某一个链表结束了,则将它的最后节点指向另外一个链表的头结点即可。
(3)代码
package swordOffer;
import charpter2.ListNode;
/**
* @author chengzhengda
* @version 1.0
* @date 2020-03-20 20:37
* @desc 两个链表的第一个公共节点
*/
public class t2 {
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode tempA = headA;
ListNode tempB = headB;
while (tempA != tempB) {
tempA = tempA == null ? headB : tempA.next;
tempB = tempB == null ? headA : tempB.next;
}
return tempA;
}
public static void main(String[] args) {
ListNode listNode11 = new ListNode(4);
ListNode listNode12 = new ListNode(1);
ListNode listNode13 = new ListNode(8);
ListNode listNode14 = new ListNode(4);
ListNode listNode15 = new ListNode(5);
listNode11.next = listNode12;
listNode12.next = listNode13;
listNode13.next = listNode14;
listNode14.next = listNode15;
ListNode listNode21 = new ListNode(5);
ListNode listNode22 = new ListNode(0);
ListNode listNode23 = new ListNode(1);
listNode21.next = listNode22;
listNode22.next = listNode23;
listNode23.next = listNode13;
ListNode res = getIntersectionNode(listNode11, listNode21);
if (res != null) {
System.out.println(res.val);
}
}
}
三、在排序数组中查找数字
(1)题目描述
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
(2)题目分析
只要是遇到排序数组中的查找,最优的解法一般都是二分法,而这题不是简单的查找,而是统计一个数字出现的次数,因此需要将二分法进行一定的改造。当中间数字小于目标数字时,则递归左数组,当中间数字大于目标数字时,则递归右数组,而当中间数字等于目标数字时,这时候需要考虑左右数组中均包含目标数字的情况,因此需要递归左右数组。这样便可统计出目标数字出现的次数。
(3)代码
package swordOffer;
/**
* @author chengzhengda
* @version 1.0
* @date 2020-03-20 21:33
* @desc 在排序数字中查找数字I
*/
public class t3 {
int num = 0;
public static void main(String[] args) {
int nums[] = {5, 7, 7, 8, 8, 10};
t3 tt = new t3();
System.out.println(tt.search(nums, 8));
}
public int search(int[] nums, int target) {
binarySearch(nums, 0, nums.length - 1, target);
return num;
}
public void binarySearch(int[] nums, int begin, int end, int tatget) {
if (begin > end) {
return;
}
int mid = (begin + end) / 2;
if (nums[mid] > tatget) {
binarySearch(nums, begin, mid - 1, tatget);
} else if (nums[mid] < tatget) {
binarySearch(nums, mid + 1, end, tatget);
} else {
num++;
binarySearch(nums, begin, mid - 1, tatget);
binarySearch(nums, mid + 1, end, tatget);
}
}
}
四、数组中重复的数字
(1)题目描述
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
(2)题目分析
最简单的方法肯定是暴力遍历法,使用一个额外的集合,当数字不在集合中时,加入集合,当集合中存在该数字时,则直接返回即可。然后这种因为使用了额外的空间,因此不是最优解。而通过分析题意可以知道,数组的长度为n,数组中的数字范围在0~n-1,很明显,可以利用桶排序的思想,当遍历到一个数字时,如果该数字不在它原本应该在的位置,则把它与它应该在的位置上的数字进行交换,当然,如果它应该在的位置上如果已经存在了这个数字,那么便是重复的数字,直接返回即可。
(3)代码
package swordOffer;
/**
* @author chengzhengda
* @version 1.0
* @date 2020-03-21 09:48
* @desc 数组中重复的数字
*/
public class t4 {
public static int findRepeatNumber1(int[] nums) {
for (int i = 0; i < nums.length; i++) {
int temp;
while (nums[i] != i) {
if (nums[i] == nums[nums[i]]) {
return nums[i];
}
temp = nums[i];
nums[i] = nums[temp];
nums[temp] = temp;
}
}
return -1;
}
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 5, 5, 6, 7};
System.out.println(findRepeatNumber1(nums));
}
}
五、二维数组中的查找
(1)题目描述
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
(2)题目分析
根据上述二维数组的特性,选择第一行的最后一个数字作为起始点,当目标数字大于该数字时,则往下移动一格,当目标数字小于该数字时,则向左移动一格,直到找到该目标数字就返回true,如果到达边界还找不到则返回false。
(3)代码
package swordOffer;
/**
* @author chengzhengda
* @version 1.0
* @date 2020-03-21 10:50
* @desc 二维数组中的查找
*/
public class t5 {
public static boolean findNumberIn2DArray(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0) {
return false;
}
int colLength = matrix.length;
int rowLength = matrix[0].length;
int i = 0;
int j = rowLength - 1;
while (i < colLength && j >= 0) {
if (target < matrix[i][j]) {
j--;
} else if (target > matrix[i][j]) {
i++;
} else {
return true;
}
}
return false;
}
public static void main(String[] args) {
int[][] matrix = {
{1, 4, 7, 11, 15},
{2, 5, 8, 12, 19},
{3, 6, 9, 16, 22},
{10, 13, 14, 17, 24},
{18, 21, 23, 26, 30}
};
System.out.println(findNumberIn2DArray(matrix, 166));
}
}