1、请列举常用的算法,并说明其时间复杂读,并说明排序思想?
①冒泡排序:量量比较待排序数据元素的大小,发现两个数据元素的次序相反时进行交换,直到没有反序的数据元素为止。时间复杂度是O(n*2)。稳定的。
public class bubble1 {
public static void main(String[] args) {
int[] array = { 7, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
bubbleSort(array);
printArray(array);
}
private static void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] > array[j]) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
public class bubble2 {
public static void main(String[] args) {
int[] array = { 7, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
bubbleSort(array);
printArray(array);
}
private static void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = array.length - 2; j >= i; j--) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
②选择排序:每一趟从待排序的数据元素中选出最小(最大)的一个元素,顺序放在已经排好序的数列的最后面,
直到全部待排序的数据元素排完,算法复杂度是O(n*2)。不稳定的。
public class select {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
selectSort(array);
printArray(array);
}
private static void selectSort(int[] array) {
int k = 0;
for (int i = 0; i < array.length; i++) {
k = i;
for (int j = i; j < array.length; j++) {
if (array[j] < array[k]) {
k = j;
}
}
int temp = array[i];
array[i] = array[k];
array[k] = temp;
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
③插入排序:每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。
算法的时间复杂度是o(n*2)。稳定的。
public class insert {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
insertSort(array);
printArray(array);
}
private static void insertSort(int[] array) {
int temp = 0;
for (int i = 1; i < array.length; i++) {
temp = array[i];
int j = 0;
for (j = i; j > 0; j--) {
if (array[j - 1] > temp) {
array[j] = array[j - 1];
} else {
break;
}
}
array[j] = temp;
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
④快速排序:在当前无序区 R[1..H]中任取一个数据元素作为比较的”基准”(不妨记为 X),用此基准将当前无序区划分为左右两个较小的无序区:R[1..I-1]和 R[I+1..H],
且左边的无序子区中数据元素均小于等于基准元素,右边的无序子区中数据元素均大于等于基准元素,而基准 X 则位于最终排序的位置上,
即R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),当 R[1..I-1]和 R[I+1..H]均非空时,分别对它们进行上述的划分过程,直至所有无序子区中的数据元素均已排序为止。
最理想情况算法时间复杂度 O(nlogn),最坏O(n^2) 。不稳定。
public class Quick {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1, 0, 2, 9, 6, 4 };
printArray(array);
quickSort(array);
printArray(array);
}
private static void quickSort(int[] array) {
quick(array, 0, array.length - 1);
}
private static void quick(int[] array, int low, int high) {
int key = 0;
if (low < high) {
key = partition(array, low, high);
quick(array, low, key - 1);
quick(array, key + 1, high);
}
}
private static int partition(int[] array, int low, int high) {
int key = array[low];
while (low < high) {
while (low < high && array[high] >= key) {
high--;
}
swap(array, low, high);
while (low < high && array[low] <= key) {
low++;
}
swap(array, low, high);
}
return low;
}
private static void swap(int[] array, int low, int high) {
int temp = array[low];
array[low] = array[high];
array[high] = temp;
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
⑤堆排序:堆排序是一树形选择排序,在排序过程中,将 R[1..N]看成是一颗完全二叉树的顺序存储结构,
利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。 算法时间复杂度O(nlogn)。不wend
public class Heap {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
heapSort(array);
printArray(array);
}
private static void heapSort(int[] array) {
if (array == null || array.length < 0) {
return;
}
buildMaxHeap(array);
for (int i = array.length - 1; i >= 1; i--) {
swap(array, 0, i);
maxHeap(array, i, 0);
}
}
private static void buildMaxHeap(int[] array) {
if (array == null || array.length < 0) {
return;
}
int half = array.length / 2;
for (int i = half; i >= 0; i--) {
maxHeap(array, array.length, i);
}
}
private static void maxHeap(int[] array, int heapSize, int index) {
int left = 2 * index + 1;
int right = 2 * index + 2;
int largest = index;
if (left < heapSize && array[left] > array[index]) {
largest = left;
}
if (right < heapSize && array[right] > array[largest]) {
largest = right;
}
if (index != largest) {
swap(array, index, largest);
maxHeap(array, heapSize, largest);
}
}
private static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
⑥希尔排序:其实就是用步长控制的插入排序,希尔排序通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而让数据项可以大幅度移动,
这样的方式可以使每次移动之后的数据离他们在最终序列中的位置相差不大,保证数据的基本有序,大大提升了排序速度,运算时间复杂度 N*logN,不稳定。
public class Shell {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1,1, 2, 9, 6, 4 };
printArray(array);
shellSort(array);
printArray(array);
}
private static void shellSort(int[] array) {
int j = 0;
int temp = 0;
for (int increment = array.length / 2; increment > 0; increment /= 2) {
for (int i = increment; i < array.length; i++) {
temp = array[i];
for (j = i; j >= increment; j -= increment) {
if (temp < array[j - increment]) {
array[j] = array[j - increment];
} else {
break;
}
}
array[j] = temp;
}
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
⑦归并排序:Divide: 把长度为 n 的输入序列分成两个长度为 n/2 的子序列。Conquer: 对这两个子序列分别采用归并排序。
Combine: 将两个排序好的子序列合并成一个最终的排序序列。时间复杂度是 O(nlogn)。 稳定的 。
public class merge {
public static void main(String[] args) {
int[] array = { 7, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
mergeSort(array);
printArray(array);
}
private static void mergeSort(int[] array) {
sort(array, 0, array.length - 1);
}
private static void sort(int[] array, int left, int right) {
if (left >= right) {
return;
}
int center = (left + right) / 2;
sort(array, left, center);
sort(array, center + 1, right);
mergesort(array, left, center, right);
}
private static void mergesort(int[] array, int left, int center, int right) {
int[] tempArray = new int[array.length];
int mid = center + 1;
int third = left;
int temp = left;
while (left <= center && mid <= right) {
if (array[left] <= array[mid]) {
tempArray[third++] = array[left++];
} else {
tempArray[third++] = array[mid++];
}
}
while (left <= center) {
tempArray[third++] = array[left++];
}
while (mid <= right) {
tempArray[third++] = array[mid++];
}
while (temp <= right) {
array[temp] = tempArray[temp++];
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
2、
快速排序的平均时间复杂度是多少?最坏时间复杂度是多少?在哪些情况下会遇到最坏的时间复杂度。
快速排序的平均时间复杂度 O(nlogn),最坏时间复杂度 O(n^2)。
最坏情况:待排序的序列为正序或者逆序。每次划分只得到一个比上一次划分少一个记录的子序列,另一个为空。如果递归树画出来,它就是一棵斜树。
当每次 pivot 选择恰好都把列表元素分成了(1,n-1)。
采取措施:pivot 的选取是通过 random 来进行 。
3、各个排序算法的稳定性,并给出理由。
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
4、两个单项链表求交点。单向链表有交点意思是交点后的节点都是一样的;因此,如果两个单向链表相交,是成 Y 字形的。
思路:求出第一个链表长 m,第二个长 n。假设 m>=n,那么就去掉第一个链表的前 m-n 个元素,使之等长,
然后依次比较第一个、第二个、第三个元素,直到找到或者结束。
public class FindNode {
public Node findNode(Node head1, Node head2) {
Node p1 = head1;
Node p2 = head2;
int i = 1, j = 1, k = 0, f = 0;
if (head1 == null || head2 == null) {
return null;
}
while (p1.next != null) {
p1 = p1.next;
i++;
}
while (p2.next != null) {
p2 = p2.next;
j++;
}
if (p1 != p2) {
return null;
} else {
p1 = head1;
p2 = head2;
f = Math.abs(i - j);
if (i > j) {
for (k = 0; k < f; k++) {
p1 = p1.next;
}
while (p1 != p2) {
p1 = p1.next;
p2 = p2.next;
}
return p1;
} else {
for (k = 0; k < f; k++) {
p2 = p2.next;
}
while (p1 != p2) {
p1 = p1.next;
p2 = p2.next;
}
return p1;
}
}
}
}
class Node {
int data;
Node next;
Node(int data) {
this.data = data;
}
}
5、
递增数列中每一项都可以表示为
3^i*5^j*7^k
(
0<=i,j,k
),即
1,3,5,7,9,15,21,25,27…
,实现算法,求出该数列中的第
n
项。
先放置几个队列:
L1: 3^i (i >=0)
L2: 3^i * 5^j (j >= 1)
L3: 3^i * 5^j * 7^k (k >= 1)
Step1: 清空三个队列、 分别把 3,5,7 放入三个队列的首位。 准备一个新队列 L(目前为空)。
Step2: 比较三个队列的头,找出最小的那个。把这个元素从队列出队,并加到 L 的尾部。
Step3:如果 Step2 中的元素是从 L1 中获取的,假设是 3^m,则在 L1 的尾部加入 3^(m+1),L2 的尾部加入 3^m*5,L3 的尾部加入 3^m*7。
如果 Step2 中的元素是从 L2 中获取的,假设是 3^m * 5^n,则在 L2 的尾部加入 3^m * 5^(n+1),L3 的尾部加入 3^m * 5^n *7。
如果 Step3 中的元素是从 L3 中获取的,假设是 3^m * 5^n * 7^p,则在 L3 的尾部加入 3^m *5^n * 7^(p+1)。
Step4: L 的长度到达 N 了吗?如果没到达,重复 Step2–Step4。如果到达,则进行 Step5。
Step5: 取得 L 的末尾元素,即为所求。
import java.util.*;
public class Print357 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] a = new int[sc.nextInt()];
a[1] = 1;
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
Deque<Integer> Q3 = new ArrayDeque<Integer>();
Deque<Integer> Q5 = new ArrayDeque<Integer>();
Deque<Integer> Q7 = new ArrayDeque<Integer>();
map.put(3, 3);
map.put(5, 5);
map.put(7, 7);
for (int i = 2; i < a.length; i++) {
if (map.isEmpty()) {
break;
}
Map.Entry<Integer, Integer> e = map.pollFirstEntry();
int key = e.getKey();
int val = e.getValue();
if (val == 5) {
Q5.add(key * 5);
map.put(Q5.pollFirst(), 5);
} else if (val == 3) {
Q5.add(key * 5);
Q3.add(key * 3);
map.put(Q3.pollFirst(), 3);
} else {
Q5.add(key * 5);
Q3.add(key * 3);
Q7.add(key * 7);
map.put(Q7.pollFirst(), 7);
}
a[i] = key;
}
while (sc.hasNext()) {
System.out.println(a[sc.nextInt()]);
}
}
}
6、
走台阶问题,一次可以走
1
,
2
,
3
级,都
N
级台阶的方法数 。
初始:f(0) = 0; f(1) =1; f(2) = 1 + 1 = 2;
递推公式:f(n) = f(n – 1) + f(n-2) + f(n – 3)
public class JumpFloor {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println(getSum(sc.nextInt()));
}
private static int getSum(int n) {
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
return getSum(n - 1) + getSum(n - 2) + getSum(n - 3);
}
}
7、
10
进制数转
2
进制数
题目:2 进制除了 0,1,还可以用 2 表示。例如:
1-> 1
2-> 10 or 02
3->11
4 ->100 or 020 or 012
问题:这样一个十进制数转为二进制数,就不是唯一的了。现求十进制数 N 转换为这种二进制数的所有表示方法数。
f(0)=1, f(1)=1, f(2)=2,
f(n) = f( (n-1)/2) 当 n 为奇数
f(n)= f(n/2)+f((n-2)/2 )当 n 为偶数
public class TenToTwo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println(getRes(sc.nextInt()));
}
private static int getRes(int n) {
if (n == 0) {
return 1;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
if (n % 2 == 1) {
return getRes((n - 1) / 2);
} else {
return getRes(n / 2) + getRes((n - 2) / 2);
}
}
}
8、
一个环状链表(收尾相连),两个指针
head1
和
head2
从同一个节点出发,
head1
每次走一步,
head2
每次走两步,请证明,两个指针第一次相遇于出发的节点。
设两个指针走的次数为 x,使用简单的数学公式即可证明。难度 1 面。考察基本的数学 知识。
设链表有 m 个元素,head1 在第一次相遇时走了 n 步,c 为 head1 和 head2 第一次相遇的节点距离出发
节点的距离。
则: head1 走过的路程为 c = n;
head2 走过的路程为 c + k *m = 2n; (k 为整数)
因此,c = k*m,即 c 恰好是链表长度的整数倍,即两个指针第一次相遇一定是在出发的节点。
9、
一个链表中含有环。请找出环的起始节点 。
让两个指针 head1 和 head2 从同一个节点出发,head1 每次走一步,head2 每次走两步,当二者重合时,让 head2 回到链表的头部,
以每次一步的步长走,当 head1 和 head2 再次相遇时,就是环路的起始节点了。
10、给定 N 个数,其中有一个数的出现次数超过N/2,请找出这个数,O( n)算法 。
private static int moreThanHalfNumber(int[] array) {
if (array == null || array.length == 0) {
return 0;
}
int mid = array.length / 2;
int start = 0;
int end = array.length - 1;
int index = partition(array, start, end);
while (index != mid) {
if (index >= mid) {
end = index - 1;
index = partition(array, start, end);
} else {
start = index + 1;
index = partition(array, start, end);
}
}
int res = array[mid];
if (!checkMoreThanHalf(array, res)) {
res = 0;
}
return res;
}
private static boolean checkMoreThanHalf(int[] array, int res) {
int times = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] == res) {
times++;
}
}
boolean isMoreThanHalf = true;
if (times < array.length / 2) {
isMoreThanHalf = false;
}
return isMoreThanHalf;
}
private static int partition(int[] array, int start, int end) {
int key = array[(int) (start + (int) Math.random() * (end - start))];
while (start < end) {
while (start < end && array[end] >= key) {
end--;
}
swap(array, start, end);
while (start < end && array[start] <= key) {
start++;
}
swap(array, start, end);
}
return start;
}
11、最长连续子序列之和(和最接近0的子序列),环形数组的最大子序列和。
12、字符串按字母 a-z 排序
题目要求:
(1)不是用排序库函数;
(2)代码实现;
(3)算法复杂度估算;
(4)测试自己的代码;
(5)能否用另一种排序方式,比较优缺点;(plus)
13、已知一个乱序的整数数组,求该数组排序相邻两数的最大间隔,要求时间复杂度为 O(n)。
14、求两个相同大小已排序数组的中位数 。
15、已知一个数组 a1, a2, …, an, b1, b2, …, bn,设计一个算法把数组变成a1, b1, a2, b2, …, an,bn。
private static int[] swapPrintArray(int[] a, int[] b) {
if (a == null) {
return b;
}
if (b == null) {
return a;
}
if (a == null && b == null) {
return null;
}
int[] res = new int[a.length + b.length];
int i = 0;
int j = 0;
int k = 0;
while (i < a.length && j < b.length) {
if (i == j) {
res[k++] = a[i++];
} else if (i > j) {
res[k++] = b[j++];
}
}
while (i < a.length) {
res[k++] = a[i++];
}
while (j < b.length) {
res[k++] = b[j++];
}
return res;
}
16、全排序算法。
全排序算法就是列举一些字符的所有排列顺序 。
private static void arrange(String[] str, int start, int len) {
if (start == len - 1) {
for (int i = 0; i < len; i++) {
System.out.print(str[i] + " ");
}
System.out.println();
total++;
} else {
for (int i = start; i < len; i++) {
swap(str, start, i);
arrange(str, start + 1, len);
swap(str, start, i);
}
}
}
private static void swap(String[] str, int i, int j) {
String tmp = new String();
tmp = str[i];
str[i] = str[j];
str[j] = tmp;
}