【面试】收集一些面试中的算法题

目录

1.将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的。

2.给定一个二叉树和一个值\ sum sum,判断是否有从根节点到叶子节点的节点值之和等于\ sum sum 的路径,例如:给出如下的二叉树,\ sum=22 sum=22,​返回true,因为存在一条路径 5\to 4\to 11\to 25→4→11→2的节点值之和为 22

3.设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能

LFU算法

4.将给出的整数x翻转。例1:x=123,返回321例2:x=-123,返回-321

 5.输入一个链表,反转链表后,输出新链表的表头。

6.求平方根:

7.合并\ k k 个已排序的链表并将其作为一个已排序的链表返回。分析并描述其复杂度。 

给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。

9.分别按照二叉树先序,中序和后序打印所有的节点。

10.给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)例如:给定的二叉树是{3,9,20,#,#,15,7},​该二叉树之字形层序遍历的结果是

11. 素数

13.判断回文数

14.二分查找

15.判断给定的链表中是否有环


1.将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的。

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param l1 ListNode类 
     * @param l2 ListNode类 
     * @return ListNode类
     */
    public ListNode mergeTwoLists (ListNode l1, ListNode l2) {

if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        ListNode mergeNode = null;
        if (l1.val < l2.val) {
            mergeNode = l1;
            mergeNode.next = mergeTwoLists(l1.next, l2);
        } else {
            mergeNode = l2;
            mergeNode.next = mergeTwoLists(l1, l2.next);
        }
        return mergeNode;
    }
}

2.给定一个二叉树和一个值\ sum sum,判断是否有从根节点到叶子节点的节点值之和等于\ sum sum 的路径,
例如:
给出如下的二叉树,\ sum=22 sum=22,

返回true,因为存在一条路径 5\to 4\to 11\to 25→4→11→2的节点值之和为 22

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if (root == null)
            return false;

        sum -= root.val;
        if (sum == 0 && root.left == null && root.right == null)
            return true;

        return hasPathSum(root.left, sum) || hasPathSum(root.right, sum);
    }
}

3.设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能

  • (The Least Recently Used,最近最久未使用算法)
  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值

[要求]

  1. set和get方法的时间复杂度为O(1)
  2. 某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
  3. 当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。

若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案

import java.util.*;


public class Solution {
    /**
     * lru design
     * @param operators int整型二维数组 the ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LRU (int[][] operators, int k) {
 LinkedHashMap<Integer,Integer> lruMap = new LinkedHashMap<Integer,Integer>();
        ArrayList<Integer> result = new ArrayList();
         
        for(int[] opat:operators){
            int key = opat[1];
            switch(opat[0]){
                case 1:
                    int value = opat[2];
                    if(lruMap.size()<k){
                        lruMap.put(key,value);
                    }else{
                        Iterator ot = lruMap.keySet().iterator();
                        lruMap.remove(ot.next());
                        lruMap.put(key,value);
                    }
                    break;
                case 2:
                    if(lruMap.containsKey(key)){
                        int val = lruMap.get(key);
                        result.add(val);
                        lruMap.remove(key);
                        lruMap.put(key,val);
                    }else{
                        result.add(-1);
                    }
                    break;
                default:
            }
        }
        int[] resultArr = new int[result.size()];
            int i=0;
            for(int a:result){
                resultArr[i++]=a;
            }
        return resultArr;
    }
}

LFU算法

LFU(Least Frequently Used ,最近最少使用算法)

一个缓存结构需要实现如下功能。

  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值

但是缓存结构中最多放K条记录,如果新的第K+1条记录要加入,就需要根据策略删掉一条记录,然后才能把新记录加入。这个策略为:在缓存结构的K条记录中,哪一个key从进入缓存结构的时刻开始,被调用set或者get的次数最少,就删掉这个key的记录;

如果调用次数最少的key有多个,上次调用发生最早的key被删除

这就是LFU缓存替换算法。实现这个结构,K作为参数给出

[要求]

set和get方法的时间复杂度为O(1)

 

若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
 

对于每个操作2,输出一个答案

package test;

import java.util.*;

class LFUCache {
    Map<Integer, Node> cache;  // 存储缓存的内容
    Map<Integer, LinkedHashSet<Node>> freqMap; // 存储每个频次对应的双向链表
    int size;
    int capacity;
    int min; // 存储当前最小频次

    public LFUCache(int capacity) {
        cache = new HashMap (capacity);
        freqMap = new HashMap();
        this.capacity = capacity;
    }
    
    public int get(int key) {
        Node node = cache.get(key);
        if (node == null) {
            return -1;
        }
        freqInc(node);
        return node.value;
    }
    
    public void put(int key, int value) {
        if (capacity == 0) {
            return;
        }
        Node node = cache.get(key);
        if (node != null) {
            node.value = value;
            freqInc(node);
        } else {
            if (size == capacity) {
                Node deadNode = removeNode();
                cache.remove(deadNode.key);
                size--;
            }
            Node newNode = new Node(key, value);
            cache.put(key, newNode);
            addNode(newNode);
            size++;     
        }
    }

    void freqInc(Node node) {
        // 从原freq对应的链表里移除, 并更新min
        int freq = node.freq;
        LinkedHashSet<Node> set = freqMap.get(freq);
        set.remove(node);
        if (freq == min && set.size() == 0) { 
            min = freq + 1;
        }
        // 加入新freq对应的链表
        node.freq++;
        LinkedHashSet<Node> newSet = freqMap.get(freq + 1);
        if (newSet == null) {
            newSet = new LinkedHashSet();
            freqMap.put(freq + 1, newSet);
        }
        newSet.add(node);
    }

    void addNode(Node node) {
        LinkedHashSet<Node> set = freqMap.get(1);
        if (set == null) {
            set = new LinkedHashSet();
            freqMap.put(1, set);
        } 
        set.add(node); 
        min = 1;
    }

    Node removeNode() {
        LinkedHashSet<Node> set = freqMap.get(min);
        Node deadNode = set.iterator().next();
        set.remove(deadNode);
        return deadNode;
    }
}

class Node {
    int key;
    int value;
    int freq = 1;

    public Node() {}
    
    public Node(int key, int value) {
        this.key = key;
        this.value = value;
    }
}

4.将给出的整数x翻转。
例1:x=123,返回321
例2:x=-123,返回-321

你有思考过下面的这些问题么?

如果整数的最后一位是0,那么输出应该是什么?比如10,100
你注意到翻转后的整数可能溢出吗?假设输入是32位整数,则将翻转10000000003就会溢出,你该怎么处理这样的样例?抛出异常?这样做很好,但是如果不允许抛出异常呢?这样的话你必须重新设计函数(比如添加一个额外的参数)。

import java.util.*;


public class Solution {
    /**
     * 
     * @param x int整型 
     * @return int整型
     */
    public int reverse (int x) {
      //本体关键点是如何判断溢出。
//推荐解答用的是用long类型存储结果,如果结果大于0x7fffffff或者小于0x80000000就溢出
//我的解法是每次计算新的结果时,再用逆运算判断与上一次循环的结果是否相同,不同就溢出
		int res=0;
		while(x!=0){
			//最后一位
			int tail=x%10;
			int newRes=res*10+tail;
			//如果newRes-tail)/10!=res说明产生了溢出
			if((newRes-tail)/10!=res)
				return 0;
			res=newRes;
			x=x/10;
		}
		return res;
	}
}

 5.
输入一个链表,反转链表后,输出新链表的表头。

 

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
       
        if(head==null)
            return null;
        //head为当前节点,如果当前节点为空的话,那就什么也不做,直接返回null;
        ListNode pre = null;
        ListNode next = null;
        //当前节点是head,pre为当前节点的前一节点,next为当前节点的下一节点
        //需要pre和next的目的是让当前节点从pre->head->next1->next2变成pre<-head next1->next2
        //即pre让节点可以反转所指方向,但反转之后如果不用next节点保存next1节点的话,此单链表就此断开了
        //所以需要用到pre和next两个节点
        //1->2->3->4->5
        //1<-2<-3 4->5
        while(head!=null){
            //做循环,如果当前节点不为空的话,始终执行此循环,此循环的目的就是让当前节点从指向next到指向pre
            //如此就可以做到反转链表的效果
            //先用next保存head的下一个节点的信息,保证单链表不会因为失去head节点的原next节点而就此断裂
            next = head.next;
            //保存完next,就可以让head从指向next变成指向pre了,代码如下
            head.next = pre;
            //head指向pre后,就继续依次反转下一个节点
            //让pre,head,next依次向后移动一个节点,继续下一次的指针反转
            pre = head;
            head = next;
        }
        //如果head为null的时候,pre就为最后一个节点了,但是链表已经反转完毕,pre就是反转后链表的第一个节点
        //直接输出pre就是我们想要得到的反转后的链表
        return pre;
    }
}

6.求平方根:

实现函数 int sqrt(int x).

计算并返回x的平方根

import java.util.*;


public class Solution {
    /**
     * 
     * @param x int整型 
     * @return int整型
     */
    public int sqrt (int x) {
       int low = 0;
        int high = x;
        while (low <= high) {
            long mid = (low + high) / 2;
            if(mid * mid == x) return (int)mid;
            else if(mid * mid < x) low = (int)(mid + 1);
            else high = (int)(mid - 1);
        }
        return high;
    }
}

7.合并\ k k 个已排序的链表并将其作为一个已排序的链表返回。分析并描述其复杂度。 

import java.util.*;


public class Solution {
    public ListNode mergeKLists(ArrayList<ListNode> lists) {
        if (lists == null || lists.size() == 0)
            return null;

        PriorityQueue<ListNode> queue = new PriorityQueue<ListNode>(lists.size(), new Comparator<ListNode>() {
            @Override
            public int compare(ListNode o1, ListNode o2) {
                if (o1.val < o2.val)
                    return -1;
                else if (o1.val == o2.val)
                    return 0;
                else
                    return 1;
            }
        });

        ListNode dummy = new ListNode(0);
        ListNode tail = dummy;

        for (ListNode node : lists)
            if (node != null)
                queue.add(node);

        while (!queue.isEmpty()) {
            tail.next = queue.poll();
            tail = tail.next;

            if (tail.next != null)
                queue.add(tail.next);
        }
        return dummy.next;
    }
}

8.

给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。

注意:

  1. 三元组(a、b、c)中的元素必须按非降序排列。(即a≤b≤c)
  2. 解集中不能包含重复的三元组。
    import java.util.Arrays;
    import java.util.ArrayList;
    
    public class Solution {
        public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
            ArrayList<ArrayList<Integer>> lists = new ArrayList<>();
            if (num == null || num.length == 0) {
                return lists;
            }
            Arrays.sort(num);
            for (int k = 0; k < num.length - 2; k++) {
                if (num[k] > 0) {
                    break;
                }
                if (k > 0 && num[k] == num[k - 1]) {
                    continue;
                }
                int i = k + 1;
                int j = num.length - 1;
                while (i < j) {
                    int sum = num[k] + num[i] + num[j];
                    if (sum > 0) {
                        while (j > i && num[j] == num[--j]);
                    } else if (sum < 0) {
                        while (i < j && num[i] == num[++i]);
                    } else {
                        ArrayList<Integer> list = new ArrayList<>();
                        list.add(num[k]);
                        list.add(num[i]);
                        list.add(num[j]);
                        lists.add(list);
                        while (i < j && num[i] == num[++i]);
                        while (j > i && num[j] == num[--j]);
                    }
                }
            }
            return lists;
        }
    }

    9.分别按照二叉树先序,中序和后序打印所有的节点。

    import java.util.*;
    
    /*
     * public class TreeNode {
     *   int val = 0;
     *   TreeNode left = null;
     *   TreeNode right = null;
     * }
     */
    
    public class Solution {
        /**
         * 
         * @param root TreeNode类 the root of binary tree
         * @return int整型二维数组
         */
        public int[][] threeOrders (TreeNode root) {
            int[][] result = new int[3][];
            preOrder(root);
            inOrder(root);
            postOrder(root);
            result[0] = preResult.stream().mapToInt(Integer::intValue).toArray();
            result[1] = inResult.stream().mapToInt(Integer::intValue).toArray();
            result[2] = postResult.stream().mapToInt(Integer::intValue).toArray();
            return result;
        }
        
        private List<Integer> preResult = new ArrayList<>();
        private List<Integer> inResult = new ArrayList<>();
        private List<Integer> postResult = new ArrayList<>();
        
        private void preOrder(TreeNode root) {
            if (root == null) {
                return;
            }
            preResult.add(root.val);
            preOrder(root.left);
            preOrder(root.right);
        }
        
        private void inOrder(TreeNode root) {
            if (root == null) {
                return;
            }
            inOrder(root.left);
            inResult.add(root.val);
            inOrder(root.right);
        }
        
        private void postOrder(TreeNode root) {
            if (root == null) {
                return;
            }
            postOrder(root.left);
            postOrder(root.right);
            postResult.add(root.val);
        }
        
    }

    10.给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
    例如:
    给定的二叉树是{3,9,20,#,#,15,7},

    该二叉树之字形层序遍历的结果是

    [

    [3],

    [20,9],

    [15,7]

    ]

    import java.util.*;
    public class Solution {
        public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) {
            //思路:层序遍历,对lists引入一个计数器
            //如果是需要逆序输出list时,引入Collections集合工具类对list进行反转
            ArrayList<ArrayList<Integer>> lists = new ArrayList<>();
            if(root == null)
                return lists;
            Queue<TreeNode> queue = new LinkedList<>();
            queue.offer(root);
            boolean flag = true;
            while(!queue.isEmpty())
            {
                ArrayList<Integer> list = new ArrayList<>();
                int size = queue.size();
                for(int i = 0; i < size; i++)
                {
                    TreeNode node = queue.poll();
                    list.add(node.val);
                    if(node.left != null)
                        queue.offer(node.left);
                    if(node.right != null)
                        queue.offer(node.right);
                }
                if(flag)
                {
                    lists.add(list);
                    flag = false;
                }
                else
                {
                    Collections.reverse(list);
                    lists.add(list);
                    flag = true;
                }
            }
            return lists;
        }
    }

     

11. 素数

    public boolean iszhishu(int x)
    {
        for(int i=2;i<=x/2;i++)
            if (x % 2==0 )
                return false;
        return true;
    }

12.水仙花数

13.判断回文数

import java.util.Scanner; 
public class Ex25 { 
	static int[] a = new int[5]; 
	static int[] b = new int[5]; 
	public static void main(String[] args) { 
		boolean is =false; 
		Scanner s = new Scanner(System.in); 
		long l = s.nextLong(); 
		if (l > 99999 || l < 10000) { 
			System.out.println("Input error, please input again!"); l = s.nextLong(); 
		} 
		for (int i = 4; i >= 0; i--) { 
			a[i] = (int) (l / (long) Math.pow(10, i)); 
			l =(l % ( long) Math.pow(10, i)); 
		} 
		System.out.println(); 
		for(int i=0,j=0; i<5; i++, j++) { 
			b[j] = a[i]; 
		}
		for(int i=0,j=4; i<5; i++, j--) { 
			if(a[i] != b[j]) { 
				is = false; break; 
			} else {
				is = true; 
			} 
		}
		if(is == false) { 
			System.out.println("is not a Palindrom!"); 
		} else if(is == true) {
			System.out.println("is a Palindrom!"); 
		}
	} 
}

14.二分查找

请实现有重复数字的有序数组的二分查找。

输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。

import java.util.*;


public class Solution {
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型一维数组 有序数组
     * @return int整型
     */
    public int upper_bound_ (int n, int v, int[] a) {
        // write code here
        int left=0;
        int right=n-1;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(a[mid]>=v)
                 right=mid;      
            else 
                left=mid+1;
        }
        if(a[left]==a[right] && a[left]==v)//这个是判断如果最后一个是目标值的话,就返回正常坐标
             return left+1;
        else  return left==n-1?n+1:left+1;//判断其他情况,因为已经判断了最后一个,所以现在如果还是=n-1就说明找完了没找到,返回n+1,其他返回正常值。
    }
}

15.判断给定的链表中是否有环

对于一个给定的链表,返回环的入口节点,如果没有环,返回null

package linkedlist;
/**
 * 题目描述: 链表的入环节点,如果无环,返回null
 * Given a linked list, return the node where the cycle begins. If there is no cycle, returnnull.
 * Follow up: Can you solve it without using extra space?
 * 思路:
 * 1)首先判断是否有环,有环时,返回相遇的节点,无环,返回null
 * 2)有环的情况下, 求链表的入环节点
 *   fast再次从头出发,每次走一步,
 *   slow从相遇点出发,每次走一步,
 *   再次相遇即为环入口点。
 * 注:此方法在牛客BAT算法课链表的部分有讲解。
 */
//nowcoder pass
public class Solution {
	
    public ListNode detectCycle(ListNode head) {
    	if (head == null) {
    		return null;
    	}
    	
    	ListNode meetNode = meetingNode(head);
    	if (meetNode == null) {//说明无环
    		return null;
    	}
    	
    	ListNode fast = head;
    	ListNode slow = meetNode;
    	while (slow != fast) {
    		slow = slow.next;
    		fast = fast.next;
    	}
    	
    	return slow;
    }
    
    //寻找相遇节点,如果无环,返回null
    public ListNode meetingNode(ListNode head) {
    	ListNode slow = head;
    	ListNode fast = head;
    	while (fast != null && fast.next != null) {
    		slow = slow.next;
    		fast = fast.next.next;
    		if (slow == fast) {
    			return slow;
    		}
    	}
    	return null;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值