java常见代码面试题

一、Java中的经典算法之冒泡排序(Bubble Sort)

 

原理:比较两个相邻的元素,将值大的元素交换至右端。

思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。

举例说明:要排序数组:int[] arr={6,3,8,2,9,1};   

第一趟排序:

    第一次排序:63比较,6大于3,交换位置:  3  6  8  2  9  1

    第二次排序:68比较,6小于8,不交换位置:3  6  8  2  9  1

    第三次排序:82比较,8大于2,交换位置:  3  6  2  8  9  1

    第四次排序:89比较,8小于9,不交换位置:3  6  2  8  9  1

    第五次排序:91比较:9大于1,交换位置:  3  6  2  8  1  9

    第一趟总共进行了5次比较, 排序结果:      3  6  2  8  1  9

---------------------------------------------------------------------

第二趟排序:

    第一次排序:36比较,3小于6,不交换位置:3  6  2  8  1  9

    第二次排序:62比较,6大于2,交换位置:  3  2  6  8  1  9

    第三次排序:68比较,6大于8,不交换位置:3  2  6  8  1  9

    第四次排序:81比较,8大于1,交换位置:  3  2  6  1  8  9

    第二趟总共进行了4次比较, 排序结果:      3  2  6  1  8  9

---------------------------------------------------------------------

第三趟排序:

    第一次排序:32比较,3大于2,交换位置:  2  3  6  1  8  9

    第二次排序:36比较,3小于6,不交换位置:2  3  6  1  8  9

    第三次排序:61比较,6大于1,交换位置:  2  3  1  6  8  9

    第二趟总共进行了3次比较, 排序结果:         2  3  1  6  8  9

---------------------------------------------------------------------

第四趟排序:

    第一次排序:23比较,2小于3,不交换位置:2  3  1  6  8  9

    第二次排序:31比较,3大于1,交换位置:  2  1  3  6  8  9

    第二趟总共进行了2次比较, 排序结果:        2  1  3  6  8  9

---------------------------------------------------------------------

第五趟排序:

    第一次排序:21比较,2大于1,交换位置:  1  2  3  6  8  9

    第二趟总共进行了1次比较, 排序结果:  1  2  3  6  8  9

---------------------------------------------------------------------

最终结果:1  2  3  6  8  9

---------------------------------------------------------------------

由此可见:N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,所以可以用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数,即

按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

冒泡排序的优点:每进行一趟排序,就会少比较一次,因为每进行一趟排序都会找出一个较大值。如上例:第一趟比较之后,排在最后的一个数一定是最大的一个数,第二趟排序的时候,只需要比较除了最后一个数以外的其他的数,同样也能找出一个最大的数排在参与第二趟比较的数后面,第三趟比较的时候,只需要比较除了最后两个数以外的其他的数,以此类推……也就是说,没进行一趟比较,每一趟少比较一次,一定程度上减少了算法的量。

用时间复杂度来说:

  1.如果我们的数据正序,只需要走一趟即可完成排序。所需的比较次数和记录移动次数均达到最小值,即:Cmin=n-1;Mmin=0;所以,冒泡排序最好的时间复杂度为O(n)。

  2.如果很不幸我们的数据是反序的,则需要进行n-1趟排序。每趟排序要进行n-i次比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:冒泡排序的最坏时间复杂度为:O(n2) 。

综上所述:冒泡排序总的平均时间复杂度为:O(n2) 。

代码实现:

 

复制代码
/*
 * 冒泡排序
 */
public class BubbleSort {
  public static void main(String[] args) {
    int[] arr={6,3,8,2,9,1};
    System.out.println("排序前数组为:");
    for(int num:arr){
      System.out.print(num+" ");
    }
    for(int i=0;i<arr.length-1;i++){//外层循环控制排序趟数
      for(int j=0;j<arr.length-1-i;j++){//内层循环控制每一趟排序多少次
        if(arr[j]>arr[j+1]){
          int temp=arr[j];
          arr[j]=arr[j+1];
          arr[j+1]=temp;
        }
      }
    } 
    System.out.println();
    System.out.println("排序后的数组为:");
     for(int num:arr){
       System.out.print(num+" ");
     } 
  }
 }
复制代码

 

五、性能分析:

若记录序列的初始状态为"正序",则冒泡排序过程只需进行一趟排序,在排序过程中只需进行n-1次比较,且不移动记录;反之,若记录序列的初始状态为"逆序",则需进行n(n-1)/2次比较和记录移动。因此冒泡排序总的时间复杂度为O(n*n)。

六、算法优化:

冒泡排序法存在的不足及改进方法:
第一,在排序过程中,执行完最后的排序后,虽然数据已全部排序完备,但程序无法判断是否完成排序,为了解决这一不足,可设置一个标志位flag,将其初始值设置为非0,表示被排序的表是一个无序的表,每一次排序开始前设置flag值为0,在进行数据交换时,修改flag为非0。在新一轮排序开始时,检查此标志,若此标志为0,表示上一次没有做过交换数据,则结束排序;否则进行排序;

复制代码
package 冒泡排序;
import java.util.Arrays;
/**
 * 冒泡排序改进版
 * @author mmz
 *
 */
public class BubbleSort1 {
    public static void BubbleSort(int[] arr) {
        boolean flag = true;
        while(flag){
            int temp;//定义一个临时变量
            for(int i=0;i<arr.length-1;i++){//冒泡趟数,n-1趟
                for(int j=0;j<arr.length-i-1;j++){
                    if(arr[j+1]<arr[j]){
                        temp = arr[j];
                        arr[j] = arr[j+1];
                        arr[j+1] = temp;
                        flag = true;
                    }
                }
                if(!flag){
                    break;//若果没有发生交换,则退出循环
                }
            }
        }
    }
    public static void main(String[] args) {
        int arr[] = new int[]{1,6,2,2,5};
        BubbleSort.BubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
复制代码

  第二、在冒泡排序中,一趟扫描有可能无数据交换,也有可能有一次或多次数据交换,在传统的冒泡排序算法及近年来的一些改进的算法中,只记录一趟扫描有无数据交换的信息,对数据交换发生的位置信息则不予处理。为了充分利用这一信息,可以在一趟全局扫描中,对每一反序数据对进行局部冒泡排序处理,称之为局部冒泡排序。局部冒泡排序与冒泡排序算法具有相同的时间复杂度,并且在正序和逆序的情况下,所需的关键字的比较次数和移动次数完全相同。由于局部冒泡排序和冒泡排序的数据移动次数总是相同的,而局部冒泡排序所需关键字的比较次数常少于冒泡排序,这意味着局部冒泡排序很可能在平均比较次数上对冒泡排序有所改进,当比较次数较少的优点不足以抵消其程序复杂度所带来的额外开销,而当数据量较大时,局部冒泡排序的时间性能则明显优于冒泡排序。对于N个无序数据,我们在进行一趟冒泡排序时,如果第k个数据和第k+1个数据逆序,那么对第k+1个数据进行一趟向前的冒泡排序,使其移动到合适的位置,也就是说让前面k+1个数据调节为正序。因为这种冒泡法只对前k+1个数据冒泡处理,所以我们称它为——局部冒泡


二。java去重复字符串

  
/**
     * @方法一:先把字符串通过空拆分为数组,再利用list集合的contains方法去数组重复项添加数组项
     * @param str
     * @return
     */
	public static String strDuprRem(String str){
		String[] strArr =str.split("");
		
		if (strArr.length == 0 ) {
            	    return null; 
       				List<String> list = new ArrayList<String>(); 
		StringBuffer sb = new StringBuffer();
		for(int i=0;i < strArr.length;i++){
			if(!list.contains(strArr[i])){
				list.add(strArr[i]);
				sb.append(strArr[i]+"");
			}
		}		
		return sb.toString().substring(0, sb.toString().lastIndexOf(""));		
	}
	
	 /**
     * @方法二:先把字符串通过字符串的toCharArray变为字符数组,
     * 再利用循环刻录字符数组每一项内容,利用循环比较刻录的每一项内容与数组每一项内容比较,相等就把内容置空,最后去处空格
     * @param str
     * @return
     */
	public static String strDuprRem2(String str){
		char[] charArr =str.toCharArray();
		if(charArr==null){
			return str;
		}
		if (charArr.length < 2 ) {
          	   return str; 
       		 }
		//双层循环把字符数组重复项置空
		for(int i=0;i<charArr.length;i++){
			char tmp =charArr[i];
			if(tmp!=' '){
				for(int j=i+1;j<charArr.length;j++){
					if(tmp==charArr[j]){
						charArr[j]=' ';
					}	
				}
			}
		}
		//字符数组去空
		StringBuffer sb = new StringBuffer();
		for(int t=0;t<charArr.length;t++){
			if(charArr[t]!=' '){
				sb.append(charArr[t]);
			}		
		}
		
		return sb.toString();		
	}
	
	/**
     * @方法三:先把字符串通过字符串的toCharArray变为字符数组,
     * 再利用循环把字符数组每一项添加到set集合,由于set集合内容不允许重复从而到达去重的目的,
     * 添加进集合的时候要做空格判断空格项不能添加进集合,遍历set集合用StringBuffer转为字符串,
     * 但是set集合是无序,看到结果第三种内容顺序跟第一种不一样,要是有顺序要求此种方法不可用
     * @param str
     * @return
     */
	public static String strDuprRem3(String str){
		char[] charArr =str.toCharArray();
		if(charArr==null){
			return str;
		}
		if (charArr.length < 2 ) {
            		 return str; 
       		 }
		
		Set<String> hash =new HashSet<String>();
		//字符数组循环添加进set集合
		for(int i=0;i<charArr.length;i++){
			if(charArr[i]!=' '){    //字符数组去空并添加到set集合
				hash.add(String.valueOf(charArr[i]));
			}
		}
//		//set集合遍历转为字符串
		StringBuffer sb = new StringBuffer();
		for(String laststr:hash){
			sb.append(laststr);
		}
		return sb.toString();		
	}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

写了两个去重复的方法,可以自己打印以下效率:

    public static void main(String[] args) {
		StrWeiLoss strloss=new StrWeiLoss();
		String chatStr1= strloss.strDuprRem("1    2 392   3   9   3  3  334scc  ccccccdcddddjdcjjjjjjjjf");
		String chatStr2= strloss.strDuprRem2("1    2 392   3   9   3  3  334scc  ccccccdcddddjdcjjjjjjjjf");
		String chatStr3= strloss.strDuprRem3("1    2 392   3   9   3  3  334scc  ccccccdcddddjdcjjjjjjjjf");
		System.out.println("第一个方法结果"+chatStr1);
		System.out.println("第二个方法结果"+chatStr2);
		System.out.println("第二个方法结果"+chatStr3);		
	}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12


对比结果是,第一个方法要比第二个方法效率高


三。java实现字符串反转

1.最简单的方法,jdk提供的reverse()方法
public static String reverse1(String str)
{
   return new StringBuffer(str).reverse().toString();

}

利用StringBuffer的对象


[java] view plain copy
  1. import java.util.*;  
  2. public class Main{  
  3.     public static void main(String[] args){  
  4.         Scanner in =new Scanner(System.in);  
  5.         while(in.hasNext()){  
  6.             String line=in.nextLine();//next()是遇到空格;nextLine()是遇到回车  
  7.             StringBuffer sb=new StringBuffer(line);  
  8.             sb.reverse();  
  9.               
  10.             System.out.println(sb.toString());  
  11.         }  
  12.     }  
  13. }  


2.最常用的方法:
public static String reverse3(String s)
 { 
   char[] array = s.toCharArray(); //将string类型参数转化为char[]类型参数
   String reverse = "";  //注意这是空串,不是null
   for (int i = array.length - 1; i >= 0; i--) 
   reverse += array[i]; 
   return reverse; 
  } 


3.常用方法的变形:
public static String reverse2(String s)

  int length = s.length(); 
  String reverse = "";  //注意这是空串,不是null
  for (int i = 0; i < length; i++) 
  reverse = s.charAt(i) + reverse;//在字符串前面连接,  而非常见的后面
  return reverse; 


4.C语言中常用的方法:
 public static String reverse5(String orig)

  char[] s = orig.toCharArray(); 
  int n = s.length - 1; 
  int halfLength = n / 2; 
  for (int i = 0; i <= halfLength; i++) { 
    char temp = s[i]; 
    s[i] = s[n - i]; 
    s[n - i] = temp; 
  } 
  return new String(s);  //知道  char数组和String相互转化

5、用栈实现String反转
public String reverseByStack(){
if (str == null || str.length() == 1){
return null ;
}
Stack<Character> stack = new Stack<Character>();
char[] ch = str.toCharArray(); //字符串转换成字符数组
for (char c : ch) {
stack.push(c); //每个字符,推进栈
}
for (int i = 0; i < ch.length; i++) {
ch[i] = stack.pop(); //移除这个堆栈的顶部对象
}
return new String(ch);
}

四。java数组去重
总结了四种方法,接下来进行展示

1、方法一

[java]  view plain  copy
  1. //数组去重方法一  
  2. String[] array = {"a","b","c","c","d","e","e","e","a"};  
  3. List<String> result = new ArrayList<>();  
  4. boolean flag;  
  5. for(int i=0;i<array.length;i++){  
  6.     flag = false;  
  7.     for(int j=0;j<result.size();j++){  
  8.         if(array[i].equals(result.get(j))){  
  9.             flag = true;  
  10.             break;  
  11.         }  
  12.     }  
  13.     if(!flag){  
  14.         result.add(array[i]);  
  15.     }  
  16. }  
  17. String[] arrayResult = (String[]) result.toArray(new String[result.size()]);  
  18. System.out.println(Arrays.toString(arrayResult));  
先遍历原数组,然后遍历结束集,通过每个数组的元素和结果集中的元素进行比对,若相同则break。若不相同,则存入结果集。
两层循环进行遍历得出最终结果。

2、方法二

[java]  view plain  copy
  1. //数组去重方法二  
  2. String[] array = {"a","b","c","c","d","e","e","e","a"};  
  3. List<String> list = new ArrayList<>();  
  4. list.add(array[0]);  
  5. for(int i=1;i<array.length;i++){  
  6.     if(list.toString().indexOf(array[i]) == -1){  
  7.             list.add(array[i]);  
  8.     }  
  9. }  
  10. String[] arrayResult = (String[]) list.toArray(new String[list.size()]);  
  11. System.out.println(Arrays.toString(arrayResult));  
通过使用indexOf方法进行判断结果集中是否存在了数组元素。

3、方法三

[java]  view plain  copy
  1. //数组去重方法三  
  2. String[] array = {"a","b","c","c","d","e","e","e","a"};  
  3. List<String> list = new ArrayList<>();  
  4. for(int i=0;i<array.length;i++){  
  5.     for(int j=i+1;j<array.length;j++){  
  6.         if(array[i] == array[j]){  
  7.             j = ++i;  
  8.         }  
  9.     }  
  10.     list.add(array[i]);  
  11. }  
  12. String[] arrayResult = (String[]) list.toArray(new String[list.size()]);  
  13. System.out.println(Arrays.toString(arrayResult));  
嵌套循环,进行比较获取满足条件结果集。

4、方法四

[java]  view plain  copy
  1.         //数组去重方法四  
  2.         String[] array = {"a","b","c","c","d","e","e","e","a"};  
  3.         Arrays.sort(array);  
  4.         List<String> list = new ArrayList<>();  
  5.         list.add(array[0]);  
  6.         for(int i=1;i<array.length;i++){  
  7.             if(!array[i].equals(list.get(list.size()-1))){  
  8.                 list.add(array[i]);  
  9.             }  
  10.         }  
  11. <pre name="code" class="java"><span style="white-space:pre">        </span>String[] arrayResult = (String[]) list.toArray(new String[list.size()]);  
System.out.println(Arrays.toString(arrayResult));
 先使用java提供的数组排序方法进行排序,然后进行一层for循环,进行相邻数据的比较即可获得最终结果集。 
      

5、方法五

[java]  view plain  copy
  1. //数组去重方法五  
  2.         String[] array = {"a","b","c","c","d","e","e","e","a"};  
  3.         Set<String> set = new HashSet<>();  
  4.         for(int i=0;i<array.length;i++){  
  5.             set.add(array[i]);  
  6.         }  
  7.         String[] arrayResult = (String[]) set.toArray(new String[set.size()]);  
  8.         System.out.println(Arrays.toString(arrayResult));  



  • 1
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值