1.寻找数组中的最大值与最小值
(1)问题分解法,把该问题看做两个独立的问题,每次分别找出最小值和最大值,一共需要遍历两次数组,比较次数为2N
(2)取单元素法,维持两个变量min和max,每次取出一个元素,先和已经找到的最小值进行比较,再与已找到的最大值进行比较,只需要遍历一次数组即可
(3)取双元素法,维持两个变量,每次比较相邻两个数,较大者与max比较,较小者与min比较,比较次数为1.5N
(4)数组元素移位法,将数组中相邻的元素分在一组,每次比较两个相邻的数,将较大值交换至这两个数左边,较小者防御右边。对大者组扫描一次找出最大值,对小者组扫描一次找出最小值,需要比较的次数为1.5N~2N,但需要改变数组结果
(5)分治法,将数组划分成两半,分别找出两边的最小值最大值,则最小值最大值分别是两边最小值的较小者、两边最大值的较大者,比较次数为1.5N
public class MaxMin {
static int MAX;
static int MIN;
public static void getMaxAndMin2(int arr[]){
MAX=arr[0];
MIN=arr[0];
for(int i=1;i<arr.length;i++){
if(arr[i]<MIN){
MIN=arr[i];
}
else if(arr[i]>MAX){
MAX=arr[i];
}
}
}
public static void getMaxAndMin3(int arr[]){
MAX=arr[0];
MIN=arr[0];
int len=arr.length;
for(int i=1;i<len-1;i=i+2){
if(i+1>len){//最后一个数
if(arr[i]>MAX){
MAX=arr[i];
}
if(arr[i]<MIN){
MIN=arr[i];
}
}
if(arr[i]>arr[i+1]){
if(arr[i]>MAX){
MAX=arr[i];
}
if(arr[i+1]<MIN){
MIN=arr[i+1];
}
}
if(arr[i]<arr[i+1]){
if(arr[i+1]>MAX){
MAX=arr[i+1];
}
if(arr[i]<MIN){
MIN=arr[i];
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] array={7,3,19,40,4,7,1};
// getMaxAndMin2(array);
getMaxAndMin3(array);
System.out.println(MAX);
System.out.println(MIN);
}
}
2.找出数组中第二大数
(1)先通过排序对数组进行排序,然后根据数组下标访问数组中第二大的数。最快的排序算法一般为快排,时间复杂度为O(nlogn),根据下标访问需要遍历一边数组,时间复杂度为O(n),所以总时间复杂度为O(nlogn)
(2)先定义两个变量:最大值,初始元素位数组首元素;二大值,初始元素为最小负整数。然后遍历数组。如果数组元素的值比最大值大,则最大值赋给二大值,最大值更改为该数组元素;如果数组元素比最大值小,最大值不变,比较该元素与二大值,如果该元素比二大值大,则将该元素赋给二大值
public class SecondMax {
public static int getSecondMax(int [] array){
int count=array.length;
int max=array[0];
int secondMax=Integer.MIN_VALUE;
for(int i=1;i<count;i++){
if(array[i]>max){
secondMax=max;
max=array[i];
}
else{
if(array[i]>secondMax){
secondMax=array[i];
}
}
}
return secondMax;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] array={7,3,19,40,4,7,1};
System.out.println(getSecondMax(array));
}
}
3.找出数组中重复元素最多的数
package array;
import java.util.*;
import java.util.Map.Entry;
/*
* 找出数组中重复元素最多的数
* eg:{1,1,2,2,4,4,4,4,5,5,6,6,6}
* 1出现2次,2出现2次,4出现4次,5出现2次,6出现3次,输出为元素4
*/
public class FindMostFrequentArray {
/*方法一:空间换时间
* 除非内存空间足够大,一般不采取
*/
/*方法二:引入Map映射表记录每一个元素出现的次数
* 判断次数大小,进而找出重复次数最多的元素
*/
public static int findMostFrequentInArray(int []a){
int result=0;
int most=0;
Map<Integer,Integer> map=new HashMap<Integer,Integer>();
for(int i=0;i<a.length;i++){
if(map.containsKey(a[i])){//map中已经包含,个数加一
map.put(a[i], map.get(a[i])+1);
}
else{//map中不包含,新加入
map.put(a[i], 1);
}
}
Iterator it=map.entrySet().iterator();
while(it.hasNext()){
Map.Entry entry=(Entry)it.next();
int key=(int)entry.getKey();
int value=(int)entry.getValue();
if(most<value){
most=value;
result=key;
}
}
return result;
// return most;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int array[]={1,5,4,3,4,4,5,4,5,5,6,6,6,6,6};
// int number=findMostFrequentInArray(array);
System.out.println(findMostFrequentInArray(array));
}
}
4.求数组中两两相加等于20的组合种数
package array;
import java.util.Arrays;
/* 求数组中两两相加等于20的组合种数
* 给定一个数组{1,7,17,2,6,3,14}
* 满足条件的有两对组合17+3=20,6+14=20
*/
public class FindSum {
/*方法一:蛮力法
* 采用两重循环遍历数组,判断两个数的和是否为20
* 时间复杂度为O(n^2)
*/
public static void findSum1(int [] a,int sum){
int len=a.length;
for(int i=0;i<len;i++){
for(int j=i;j<len;j++){
if(a[i]+a[j]==sum){
System.out.println(a[i]+","+a[j]);
}
}
}
}
/*方法二:排序法
* 先对数组元素进行排序,可选择堆排序或者快排,时间复杂度为O(nlogn)
* 然后对排序后的数组分别从前到后和从后到前遍历,从前往后遍历begin,从后往前end
* 当arr[begin]+arr[end]<20时,如果存在两个数和为20,那么这两个数一定在[begin+1,end]
* 当arr[begin]+arr[end]>20时,如果存在两个数和为20,那么这两个数一定在[begin,end+1]
* 这个过程的时间复杂度为O(n)
*/
public static void findSum2(int [] a,int sum){
Arrays.sort(a);
int begin=0;
int end=a.length-1;
while(begin<end){
if(a[begin]+a[end]<sum){
begin++;
}
else if(a[begin]+a[end]>sum){
end--;
}
else{
System.out.println(a[begin]+","+a[end]);
begin++;
end--;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int []array={1,7,17,2,6,3,14};
// findSum1(array,20);
findSum2(array,20);
}
}
5.数组循环右移k位
package array;
/*
* 把一个数组循环右移k位
* eg:12345678循环右移2位变为78123456
* 1.逆序数组子序列123456,变为65432178
* 2.逆序数组子序列78,变为65432187
* 3.全部逆序,变为78123456
* 该算法进行了三次逆序操作,时间复杂度为O(n)
*/
public class ReverseShift {
public static void reverse(int [] a,int b,int e){
for(;b<e;b++,e--){
int temp=a[e];
a[e]=a[b];
a[b]=temp;
}
}
public static void shift_k(int []a,int k){
int n=a.length;
k=k%n;//为了防止k比n大,右移k为和右移k%n结果是一样的
reverse(a,n-k,n-1);
reverse(a,0,n-k-1);
reverse(a,0,n-1);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int array[]={1,2,3,4,5,6,7,8};
shift_k(array,2);
for(int i:array){
System.out.print(i+" ");
}
}
}
LeetCode上有道题,给定一个链表,将链表向右旋转k个位置,其中k为非负数,eg:
Given1->2->3->4->5->NULLand k =2,
return4->5->1->2->3->NULL
public static ListNode test(ListNode head,int k){
if(k==0 || head==null || head.next==null){
return head;
}
ListNode preHead=new ListNode(0);
preHead.next=head;
ListNode cur=head;
ListNode pre=head;
int total=0;
for(total=1;cur.next!=null;total++){//求出链表长度
cur=cur.next;
}
for(int i=1;i<total-k%total;i++){//向前走length-n
pre=pre.next;
}
cur.next=preHead.next;
preHead.next=pre.next;
pre.next=null;
return preHead.next;
}
6.找出数组中第k个最小的数
package array;
public class MinK {
/*
* 方法一:排序,排序后数组第k-1位置上的数即为数组的第k个最小的数
* 最好的时间复杂度为O(nlogn)
*/
/*
* 方法二:剪枝法,采用快排思想实现
* 选一个数temp=a[n-1]作为枢纽,把比它小的数放在它的左边,比它大的数放在右边
* 然后判断它的位置,如果等于k-1,那么它就是第k个最小的数
* 如果它的位置小于k-1,说明第k个小的元素一定在右边,采用递归方法在数组右边继续查找
*/
public static int quikSort(int [] array, int low, int high, int k){
int i,j,temp;
if(low>high){
return Integer.MIN_VALUE;
}
i=low;
j=high;
temp=array[i];
while(i<j){
while(i<j && array[j]>=temp){
j--;
}
if(i<j){//比中轴小的记录移到低端
array[i++]=array[j];
}
while(i<j && array[i]<temp){
i++;
}
if(i<j){//比中轴大的记录移到高端
array[j--]=array[i];
}
}
array[i]=temp;//中轴记录到尾
if(i==k-1){
return temp;
}
else if(i>k-1){
return quikSort(array,low,i-1,k);
}
else{
return quikSort(array,i+1,high,k);
}
}
public static int getKMin(int []array, int k){
if(array==null){
return Integer.MIN_VALUE;
}
if(array.length<k){
return Integer.MIN_VALUE;
}
return quikSort(array,0,array.length-1,k);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int []a={1,5,2,6,8,0,6};
System.out.println(getKMin(a,1));
System.out.println(getKMin(a,2));
System.out.println(getKMin(a,3));
System.out.println(getKMin(a,4));
}
}
7.找出数组中只出现一次的数字
package array;
/*
* 找出数组中只出现一次的数字
*/
public class FindOnce {
/*
* 问题:一个整型数组除了一个数字之外,其他数字都出现了两次(偶数),找出这个只出现一次的数字
* 要求:时间复杂度是O(n),空间复杂度是O(1)
* 排序:从第一个开始遍历,比较相邻的两个数,时间复杂度为O(nlogn)
* 异或:任何一个数字异或自己都为0,异或0都为自己
*/
public static int findOnceTwo(int []a){
int result=a[0];
for(int i=1;i<a.length;i++){
result=result^a[i];
}
return result;
}
/*
* 问题:一个整型数组除了一个数字之外,其他数字都出现了三次,找出这个只出现一次的数字
* 如果数组中所有数字出现n次,所有数字对应的二进制数中,各个位上1出现的个数均可被n整除,eg:{1,1,1,2,2,2}
* 二进制:01,01,01,10,10,10,第0位有3个1,第1位有3个1,是3的倍数
* 所以:假设出现一次的该数为a,那么去掉a后其他所有数字对应的二进制数的每个位置出现的个数为3的倍数
* 可以对数组中所有数字对应的二进制数中各个位置上的1的个数对取余,就可以得到出现1次的这个数的二进制表示
* 既适合于奇数,也适合于偶数
*/
public static int findOnce(int []a,int appearTimes){
int n=a.length;
int []bitCount=new int[32];
//计算数组中所有数组对应的二进制数各个位置上出现1的次数
for(int i=0;i<n;i++){
for(int j=0;j<32;j++){
bitCount[j]+=((a[i]>>j)&1);//>>右移
}
}
//若某位上的结果不能被整除,则肯定目标数字在这一位上
int appearOne=0;
for(int i=0;i<32;i++){
if(bitCount[i]%appearTimes!=0){//<<左移
appearOne+=(1<<i);
}
}
return appearOne;
}
public static void main(String [] args){
int [] array={1,2,3,2,4,3,5,4,1};
int [] a={1,2,1,2,4,2,4,4,1,3};
System.out.println(findOnceTwo(array));
System.out.println(findOnce(a,3));
}
}
8.递归求解一个整数数组的最大元素
最大值求法在1中已经进行了介绍,递归求法是:递归的求解“数组第一个元素”与“数组中其他元素组成的子数组的最大值”的最大值
public class MaxDiGui {
public static int max(int a,int b){
return a>b?a:b;
}
public static int maxnum(int []a,int begin){
int length=a.length-begin;
if(length==1){
return a[begin];
}
else{
return max(a[begin],maxnum(a,begin+1));
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] num={0,16,2,3,4,5,6,7,8};
System.out.println(maxnum(num,0));
}
}