数据结构面试题:数组
1. 在一个给定的从1到100的整型数组中,如何快速找到缺失的数字
如元素无重复,缺失一个数字时可采用下面方法:
二分法:假设有序,如无需先排序,二分查找,时间复杂度
O(logN)
。
求和法:1到100之和 - 数组数字之和 = 缺失数字,时间复杂度:
O(n)
,空间复杂度:
O(1)
。
异或法:依次位异或1到100,然后继续位异或数组所有数字,结果即为缺失数字,时间复杂度:
O(n)
,空间复杂度:
O(1)
。
Hash法:对数组数据Hash,可以使用BitSet,时间复杂度
O(n)
,空间复杂度
O(n)
。
扩展1:数组中缺失两个数
先排序,然后将原数据拆分成两部分,使用原始问题方法即可。
如求min~max之和
S1
,数组元素和
S2
,设缺失元素为
a
、
b
,则
S1-S2 = a+b
,以
(a+b)/2
为界将排序后的原数据拆分成两部分,问题即变为原始问题。
扩展2:范围1到100,99个出现了偶数次,1个奇数次,求奇数次的数
数组全部元素位异或,结果即为所求。
扩展3:范围1到100,98个出现了偶数次,2个奇数次,求奇数次的数
设两个出现奇数次的数分别为a、b,则数组全部元素位异或结果
c
等于
a^b
,则
c
中至少有一位为1(如无则说明两个数相等,不符合题意)。
接着对
c
进行分析,如果
c
中某一位为1,则说明
a
、
b
两个数在此位上一个为0,一个为1,因此以此位为界,将原数据分成两部分,问题即变为扩展2。
例如范围1~5的原始数据 [1, 3, 2, 5, 4, 2, 6, 6, 3, 5],全部位异或后
c=5=a^b
,二进制为
0101
,如将最低位1作为界对原数据分成 [1, 3, 5, 3, 5] 和 [2, 4, 2, 6, 6] 两部分,求最低位1的方法
c-(c&(c-1))
,此时使用扩展2方法即可。
2. 如何在不使用Java Collection API的情况下从数组中删除重复项
先排序,然后遍历移除连续出现的重复数据,可直接在原数组上将重复数据置为0(假设原数据中没有0),或者借助临时数组。
3. 在一个未排序的整型数组中,如何找到最大和最小的数字
定义max、min分别保存数值范围的最小值及最大值,循环比较并记录max、min的值即可。
4. 找出一个数组中的两个数字,使这两个数字之和等于一个给定的值
直接穷举,从数组中任意取出两个数字,计算两者之和是否为给定的数字。显然其时间复杂度为
O(n(n-1)/2)
即
O(n^2)
。
使用Hash表,根据Hash表映射查找一个数字是否存在。遍历数组,使用给定值减去数组元素,接着拍段差值是否存在于Hash表中,时间复杂度可降为
O(n)
,但会根据数组元素的取值范围使用过大的存储空间。
首先对数组进行排序,时间复杂度为
O(n*log2n)
。然后令
i=0
,
j=n-1
,判断
arr[i]+arr[j]
是否等于给定数字,如果是则
arr[i]
、
arr[j]
即为所求。如果小于给定数字则
i+=1
,否则
j-=1
。这样只需在排序后的数组上遍历一次,就可求得结果,时间复杂度为
O(n)
。两步加起来总的时间复杂度
O(n*log2n)
。
5. 如果一个数组包含多个重复元素,如何找到这些重复的元素
使用二重循环两两比较得到重复元素,时间复杂度为
O(n^2)
。
for (int i = 0; i < names.length; i++) {
for (int j = i + 1 ; j < names.length; j++) {
if (names[i].equals(names[j])) {
使用Set
,因为Set
不允许插入重复元素,可通过add()
的返回值判断元素是否重复,时间复杂度为 O(n)
。
for (String name : names) {
if (set.add(name) == false) {
使用哈希表,遍历数组元素通过数组元素值与元素数量构建哈希表,再遍历哈希表输出数量非1的元素即为重复元素。这种方法时间复杂度为 O(n)
,与方法二相同,但却能够准确求到重复数量。
for (String name : names) {
Integer count = nameAndCount.get(name);
if (count == null) {
nameAndCount.put(name, 1);
} else {
nameAndCount.put(name, ++count);
Set<Entry<String, Integer>> entrySet = nameAndCount.entrySet();
for (Entry<String, Integer> entry : entrySet) {
if (entry.getValue() > 1) {
System.out.printf("duplicate element '%s' and count '%d' :", entry.getKey(), entry.getValue());
6. 用Java从一个给定数组中删除重复元素
不借助数据结构,申请一个跟给定数组一样大小的新数组,遍历给定数组将给定数组中的每一个元素放入新数组中,放入时遍历新数组判断是否已存在,时间复杂度为 O(n^2)
。
使用List
的contains
,本质上跟方法一一样。
int[] attr = { 1, 2, 3, 3, 5, 5, 7, 9 };
List<Integer> list = new ArrayList<Integer>();
for (int i : attr) {
if (!list.contains(i)) {
list.add(i);
使用Set
或LinkedHashSet
,特别是LinkedHashSet
,因为它能够保持元素是有序的,时间复杂度为 O(n)
。
先排序再遍历放入新数组中,可根据数组有序的特性快速过滤掉重复元素,时间复杂度由排序算法决定。
7. 如何利用快速排序对一个整型数组进行排序
快速排序过程如下。
public class QuickSortDemo{
public static void main(String args[]) {
int[] unsorted = {6, 5, 3, 1, 8, 7, 2, 4};
System.out.println("Unsorted array :" + Arrays.toString(unsorted));
QuickSort algorithm = new QuickSort();
algorithm.sort(unsorted);
System.out.println("Sorted array :" + Arrays.toString(unsorted));
class QuickSort {
private int input[];
private int length;
public void sort(int[] numbers) {
if (numbers == null || numbers.length == 0) {
return;
this.input = numbers;
length = numbers.length;
quickSort(0, length - 1);
* This method implements in-place quicksort algorithm recursively.
private void quickSort(int low, int high) {
int i = low;
int j = high;
int pivot = input[low + (high - low) / 2];
while (i <= j) {
* As shown in above image, In each iteration, we will identify a
* number from left side which is greater then the pivot value, and
* a number from right side which is less then the pivot value. Once
* search is complete, we can swap both numbers.
while (input[i] < pivot) {
i++;
while (input[j] > pivot) {
j--;
if (i <= j) {
swap(i, j);
i++;
j--;
if (low < j) {
quickSort(low, j);
if (i < high) {
quickSort(i, high);
private void swap(int i, int j) {
int temp = input[i];
input[i] = input[j];
input[j] = temp;
Output :
Unsorted array :[6, 5, 3, 1, 8, 7, 2, 4]
Sorted array :[1, 2, 3, 4, 5, 6, 7, 8]
8. 用 Java 实现数组反转
数组第一个元素与最后一个元素交换,第二个元素与倒数第二个元素交换,时间复杂度为 O(n/2)
或O(n)
,空间复杂度O(1)
。