常见算法面试题

算法(面试题)

1. 反转链表

输入一个链表,反转链表后,输出新链表的表头。
输入{1,2,3}返回值{3,2,1}

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/  // 结构体(链表节点)
public class Solution {
    public ListNode ReverseList(ListNode head) {
if(head==null) {
            return null;
        }
        ListNode newHead=null;
        ListNode cur=head;
         
        ListNode pre=null;
        while(cur!=null) {
            ListNode curNext=cur.next;
            if(curNext==null) {
                newHead=cur;
            }
            cur.next=pre;
            pre=cur;
            cur=curNext;
        }
        return newHead;

    }
}

2. 栈-输出最大值,最小值,时间复杂度O(1)

提高时间效率的一个常用方法就是牺牲空间换取时间,这里也可以使用这种办法。我们可以定义一个辅助栈minStack,帮助我们记录最小值。在我们的类中,需要有两个栈,一个就是我们用来存值的栈dataStack,另外一个就是帮助我们维护最小值的栈minStack。
  push入栈操作有以下两种情况:
• dataStack为空:此时,栈中没有元素,我们将push传入的参数直接放入到dataStack以及minStack中;
• dataStack不为空:此时,将push操作传入的参数先放入dataStack中,然后判断这个元素与minStack的栈顶元素谁更大,若这个参数小于或等于minStack的栈顶元素,我们就将它加入到minStack中,否则minStack不变;
  这样,我们就可以保证,minStack的栈顶元素,一定是当前栈中最小的元素,而当我们调用min方法时,直接返回minStack的栈顶元素就行了。
  与push操作相对应的,pop出栈操作,也有两种情况:
• 出栈的元素大于栈中最小值:此时dataStack的栈顶元素出栈,而minStack不变;
• 出栈的元素等于栈中最小值:此时dataStack的栈顶元素出栈,同时,minStack的栈顶元素也出栈;

	public class Solution {
	
	    private Stack<Integer> dataStack = new Stack<>();
	    private Stack<Integer> minStack = new Stack<>();
	
	    /**
	    * 入栈操作
	    */
	    public void push(int node) {
	        // 判断是否需要更新minStack
	        if(dataStack.isEmpty() || minStack.peek() >= node) {
	            minStack.push(node);
	        }
	        // 将元素放入dataStack
	        dataStack.push(node);
	    }
	
	    /**
	    * 出栈操作
	    */
	    public void pop() {
	        // 若栈不为空才执行出栈
	        if(!dataStack.isEmpty()) {
	            // 若当前出栈的元素,等于栈中的最小值(即minStack的栈顶)
	            // 则minStack的栈顶出栈
	            if(dataStack.pop() == minStack.peek()) {
	                minStack.pop();
	            }
	        }
	    }
	
	    /**
	    * 查看栈顶元素
	    */
	    public int top() {
	        return dataStack.peek();
	    }
	
	    /**
	    * 返回栈中最小值
	    */
	    public int min() {
	        // 返回最小值,即minStack的栈顶元素
	        return minStack.peek();
	    }
	}

3.相邻岛屿问题

给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。
岛屿: 相邻陆地可以组成一个岛屿(相邻:上下左右) 判断岛屿个数。
示例1
输入
[[1,1,0,0,0],[0,1,0,1,1],[0,0,0,1,1],[0,0,0,0,0],[0,0,1,1,1]]
返回值
3

4.手写生产消费

仓库:Warehouse

public class Warehouse {
    private volatile int apple = 0;
    public synchronized void increace() {
        while (apple == 5) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        apple++;
        System.out.println("苹果生产成功!");
        notify();
    }
    public synchronized void decreace() {
        while (apple == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        apple--;
        System.out.println("苹果消费成功!");
        notify();
    }
    public static void main(String[] args) {
        Warehouse warehouse = new Warehouse();
        Consumer con = new Consumer(warehouse);
        Producer pro = new Producer(warehouse);
        Thread t1 = new Thread(con);
        Thread t2 = new Thread(pro);
        t1.start();
        t2.start();
    }
}

生产者:Producer

public class Producer implements Runnable {
    private Warehouse warehouse;
    public Producer(Warehouse warehouse) {
        this.warehouse = warehouse;
    }
    @Override
    public void run() {
        for( int i=0;i<10;i++)
        {
            try {
                System. out .println("pro  i:" +i);
                Thread. sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            warehouse.increace();
        }
    }
}

消费者:Consumer

public class Consumer implements Runnable {
    private Warehouse warehouse;
    public Consumer(Warehouse warehouse) {
        this.warehouse = warehouse;
    }
    @Override
    public void run() {
        for( int i=0;i<10;i++)
        {
            try {
                System. out .println("Con: i " +i);
                // 这里设置跟上面30不同是为了 仓库中的苹果能够增加,不会生产一个马上被消费
                Thread. sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            warehouse.decreace();
        }
    }
}

5.排序算法及稳定性

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,则是稳定的
1)冒泡排序
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

public class demo_sort {
    public static void main(String[] args) {
        //冒泡排序算法
        int[] numbers=new int[]{1,5,8,2,3,9,4};
        //需进行length-1次冒泡
        for(int i=0;i<numbers.length-1;i++)
        {
            for(int j=0;j<numbers.length-1-i;j++)
            {
                if(numbers[j]>numbers[j+1])
                {
                    int temp=numbers[j];
                    numbers[j]=numbers[j+1];
                    numbers[j+1]=temp;
                }
            }
        }
        System.out.println("从小到大排序后的结果是:");
        for(int i=0;i<numbers.length;i++)
            System.out.print(numbers[i]+" ");
    }
}

(2)选择排序
选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n - 1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

public static void main(String[] args) {
        int[] arr={1,3,2,45,65,33,12};
        System.out.println("交换之前:");
        for(int num:arr){
            System.out.print(num+" ");
        }        
        //选择排序的优化
        for(int i = 0; i < arr.length - 1; i++) {// 做第i趟排序
            int k = i;
            for(int j = k + 1; j < arr.length; j++){// 选最小的记录
                if(arr[j] < arr[k]){ 
                    k = j; //记下目前找到的最小值所在的位置
                }
            }
            //在内层循环结束,也就是找到本轮循环的最小的数以后,再进行交换
            if(i != k){  //交换a[i]和a[k]
                int temp = arr[i];
                arr[i] = arr[k];
                arr[k] = temp;
            }    
        }
        System.out.println();
        System.out.println("交换后:");
        for(int num:arr){
            System.out.print(num+" ");
        }
    }

(3)插入排序
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

public static int[] sort(int[] ins){
		
		for(int i=1; i<ins.length; i++){
			for(int j=i; j>0; j--){
				if(ins[j]<ins[j-1]){
					int temp = ins[j-1];
					ins[j-1] = ins[j];
					ins[j] = temp;
				}
			}
		}
		return ins;
	}

(4)快速排序

快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j,交换a[i]和a[j],重复上面的过程,直到i > j。 交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为5 3 3 4 3 8 9 10 11,现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j] 交换的时刻。
详细请看快速排序

public class QuickSort {
    public static void quickSort(int[] arr,int low,int high){
        int i,j,temp,t;
        if(low>high){
            return;
        }
        i=low;
        j=high;
        //temp就是基准位
        temp = arr[low];
 
        while (i<j) {
            //先看右边,依次往左递减
            while (temp<=arr[j]&&i<j) {
                j--;
            }
            //再看左边,依次往右递增
            while (temp>=arr[i]&&i<j) {
                i++;
            }
            //如果满足条件则交换
            if (i<j) {
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }
 
        }
        //最后将基准为与i和j相等位置的数字交换
         arr[low] = arr[i];
         arr[i] = temp;
        //递归调用左半数组
        quickSort(arr, low, j-1);
        //递归调用右半数组
        quickSort(arr, j+1, high);
    }
 
 
    public static void main(String[] args){
        int[] arr = {10,7,2,4,7,62,3,4,2,1,8,9,19};
        quickSort(arr, 0, arr.length-1);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

6. Java 树的遍历

前序,中序,后序,层序遍历

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

also&lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值