数据结构与算法之常见的算法笔试题

本文精选了Java开发中常见的算法题目,包括递归调用、冒泡排序、选择排序、字符串匹配、单链表反转等典型算法案例及其实现代码。

每次面试,都难免要考一些数据结构的一些算法题,尤其是对于做Java开发的程序员来说那是更加伤心,每次想跳槽都败在算法题上;所以谨以此献给Java开发人员。特别声明,这些代码很多是从网上收集来的,无意抄袭。

1、递归调用

public class Test {
	
	public static void main(String[] args) {
		
		while(true){
			System.out.println("请输入一个正整数:");
			Scanner sc=new Scanner(System.in);
			int n=sc.nextInt();
			int result=calculateFactorial(n);
			System.out.println(n+"的阶乘为:"+result);
		}

	}
	
	public static int calculateFactorial(int n){
		if(n==0||n==1){
			return 1;
		}else{
			return n*calculateFactorial(n-1);
		}
	}

}

2、冒泡排序

(1)冒泡排序算法的运作如下:(从后往前)

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

    (2)算法复杂度
    若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数
     和记录移动次数
     均达到最小值:
     ,
     。
    所以,冒泡排序最好的 时间复杂度
     。
      若初始文件是反序的,需要进行
     趟排序。每趟排序要进行
     次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:
    冒泡排序的最坏时间复杂度为
     。
    综上,因此冒泡排序总的平均时间复杂度为
     。

    算法稳定性

    冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

public class Test2 {
	
	public static void main(String[] args) {
		
		/**
		 * 冒泡排序(时间复杂度O(n^2)),从小到大排序
		 * 1.比较相邻的元素。如果第一个比第二个大,就交换他们两个
		 * 2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对
		 * 3.针对所有的元素重复以上的步骤,除了最后一个。
		 * 4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较
		 * */
		int[] a=new int[]{34,22,11,6,9,16,55};
		int n=a.length;
		for(int i=1;i<=n;i++){
			for(int j=1;j<n-i+1;j++){
				if(a[j-1]>a[j]){
					int temp=a[j-1];
					a[j-1]=a[j];
					a[j]=temp;
				}
			}
		}
		System.out.println("排序之后的数组:");
		for (int i = 0; i < n; i++) {
			System.out.println(a[i]);
		}
<span style="white-space:pre">	</span>}
}



3、选择排序

   (1)算法原理

            对比数组中前一个元素跟后一个元素的大小,如果后面的元素比前面的元素小则用一个变量k来记住他的位置,接着第二次比较,前面“后一个元素”现变成了“前一个元素”,继续跟他的“后一个元素”进行比较如果后面的元素比他要小则用变量k记住它在数组中的位置(下标),等到循环结束的时候,我们应该找到了最小的那个数的下标了,然后进行判断,如果这个元素的下标不是第一个元素的下标,就让第一个元素跟他交换一下值,这样就找到整个数组中最小的数了。然后找到数组中第二小的数,让他跟数组中第二个元素交换一下值,以此类推。

public class Test6 {
	
	public static void main(String[] args) {
		//选择排序,时间复杂度O(n^2)
		int[] b=new int[]{33,22,15,3,6,18,28};
		int n2=b.length;
		for(int i=0;i<n2;i++){
			int min=i;
			for(int j=i+1;j<n2;j++){
				if(b[min]>b[j]){
					min=j;
				}
			}
			if(min!=i){
				int temp=b[i];
				b[i]=b[min];
				b[min]=temp;
				
			}
		}
		System.out.println("选择排序之后:");
		for (int i = 0; i < b.length; i++) {
			System.out.println(b[i]);
		}
	}

}
4、计算两个字符串最长相同子串的长度

public class StringCompare {
	
	/**
	 * 思路:
	 * 1、首先寻找两个字符串中第一个相同的字母,并且计数加1
	 * 2、如果找到第一个相同的字母后,比较两个字符串相同字母索引后的字母是否相同,相同则计数加1;不同则停止比较,进入第三部
	 * 3、比较计数是否比之前的大,比之前大就保存计数和相同字符串的第一个字母索引,并且把下标和计数恢复初始值。
	 * 4、一直循环步骤1,2,3知道循环结束
	 * 5、输出相同的字符串。
	 * */
	public static void main(String[] args) {
		
		String str1="ISANSIASPDBNJPOIJNHGF";
		String str2="IOSPAOSISPDBIPOIJNIO";
		char[] ch1=str1.toCharArray();
		char[] ch2=str2.toCharArray();
		
		int count=0,firstIndex=-1,max=0,tempIndex=-1;
		for (int i = 0; i < ch1.length-max; i++) {
			
			for (int j = 0; j < ch2.length-max; j++) {
				
				if(ch1[i]==ch2[j]){

					firstIndex=i;
					count++;
					
					for (int h = i+1,q=j+1; h < ch1.length&&q<ch2.length; h++,q++) {
						if(ch1[h]==ch2[q]){
							count++;
						}else{
							break;
						}
					}
				}
				if(count>max){
					tempIndex=firstIndex;
					max=count;
				}
				count=0;
				firstIndex=-1;
			}
		}
		
		char[] ch=new char[max];
		if(tempIndex!=-1&&max>=1){
			for (int i = 0; i < max; i++) {
				ch[i]=ch1[tempIndex+i];
			}
		}
		System.out.println("最长相同子串的长度为:"+max);
		System.out.println("最长相同子串为:"+new String(ch));
	}

}



5、用Java实现求一个字符串中连续出现次数最多的子串

运用了一个叫做后缀数组的思想。

“abcabcbacjhkeeeehgue”,可以排列如下:

abcabcbacjhkeeeehgue

bcabcbacjhkeeeehgue

cabcbacjhkeeeehgue

abcbacjhkeeeehgue

bcbacjhkeeeehgue

cbacjhkeeeehgue

。。。

e


public class Test3 {
	
	public static void main(String[] args) {
		

		String str = "abcabcabcccccdefefefefefef";

	    int len = str.length();
	    int maxCount = 0;
	    String longest = "";

	    for(int pos1 = 0; pos1 < len; pos1++)
	        for(int pos2 = pos1 + 1; pos2 < len; pos2++){
	        	
	        	if((2*pos2-pos1)>len){
	        		continue;
	        	}
	        	
	            if(str.substring(pos1,pos2).equals(str.substring(pos2,2*pos2-pos1))){
	                int offset = pos2-pos1;
	                int count = 2;
	                for(int k = pos2 + offset; k <= len; k += offset){
	                	
	                	if((pos1+offset)>len||(k+offset)>len){
	                		continue;
	                	}
	                    if(str.substring(pos1,pos1+offset).equals(str.substring(k,k+offset))){
	                        count += 1;
	                    }else{
	                        break;
	                    }
	                }
	                if(count > maxCount){
	                    maxCount = count;
	                    longest = str.substring(pos1,pos1+offset);
	                }
	            }
	        }
		
		System.out.println("maxCount:"+maxCount+",longest:"+longest);
		
	}

}

6、单向链表反转

单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;链表是使用指针进行构造的列表;又称为结点列表,因为链表是由一个个结点组装起来的;其中每个结点都有指针成员变量指向列表中的下一个结点;列表是由结点构成,head指针指向第一个成为表头结点,而终止于最后一个指向nuLL的指针。

实现:

1、首先新建一个节点类Node.java

节点的特点:存储数据和指向下一个节点的指针

package com.hua.linkedlist;
/**
 * @description:定义一个节点类,包括数据,和一个指针
 * @date:(2015-8-12 下午10:36:17)
 * @author Administrator
 * @version v1.0
 * @since v1.0
 *
 * Modified history
 *
 *    Modified date:  
 *    Modifier user:     
 *    description: 
 *
 * */
public class Node {
	
	private int data;//数据
	
	private Node nextNode;//下一个节点
	
	public Node(){
		
	}
	
	public Node(int record){
		this.data=record;
	}

	public int getData() {
		return data;
	}

	public void setData(int data) {
		this.data = data;
	}

	public Node getNextNode() {
		return nextNode;
	}

	public void setNextNode(Node nextNode) {
		this.nextNode = nextNode;
	}
	
	
	

}
2、反转类ReverseSingleList.java

反转方式有两种:一种是采用递归,一种是普通的循环反转每一个节点指针。

public class ReverseSingleList {
	
	/**
	 * 常规方法反转单链表
	 * */
	public static Node reverseSingleList(Node head){
		
		//head为头指针
		if(head==null){
			return null;
		}
		
		Node pre=head;
		Node cur=head.getNextNode();
		Node next;
		while(cur!=null){
			next=cur.getNextNode();
			cur.setNextNode(pre);
			pre=cur;
			cur=next;
		}
		
		//将原链表的头结点的下一个节点的置为null,再将反转后的头结点赋给head
		head.setNextNode(null);
		head=pre;
		return head;
		
	}
	
	/**
	 * 利用递归实现单向链表反转
	 * */
	public static Node reverseSingleList2(Node head){
		if(head==null||head.getNextNode()==null){
			return head;
		}
		Node reversedHead =reverseSingleList2(head.getNextNode());
		head.getNextNode().setNextNode(head);
		head.setNextNode(null);
		return reversedHead;
	}
	
	public static void main(String[] args) {
		
		Node head=new Node(0);
		Node cur=null;
		Node temp=null;
		
		//构造一个长度为10的链表,保存头结点为head
		for(int i=1;i<10;i++){
			
			temp=new Node(i);
			if(i==1){
				head.setNextNode(temp);
			}else{
				cur.setNextNode(temp);
			}
			cur=temp;
		}
		//打印反转前的链表
		System.out.println("链表反转前的数据:");
		Node h=head;
		while(h!=null){
			System.out.print(h.getData()+" ");
			h=h.getNextNode();
		}
		//调用反转方法
		System.out.println("\r\n链表反转前的数据:");
		head=reverseSingleList2(head);
		while(head!=null){
			System.out.print(head.getData()+" ");
			head=head.getNextNode();
		}
		
	}

}

最后希望大家能够把平常见到的算法题共享出来。





评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值