欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一些算法编程题整理

程序员文章站 2022-07-15 08:11:12
...

1.在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

  • 思路:
  • 矩阵是有序的,从左下角来看,向上数字递减,向右数字递增,
  • 因此从左下角开始查找,当要查找数字比左下角数字大时。右移
  • 要查找数字比左下角数字小时,上移
public class Solution {
public boolean Find(int [][] array,int target) {
int len = array.length-1;
        int i = 0;
        while((len >= 0)&& (i < array[0].length)){
            if(array[len][i] > target){
                len--;
            }else if(array[len][i] < target){
                i++;
            }else{
                return true;
            }
        }
        return false;
    }
}

2.请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

public class Solution {
    public String replaceSpace(StringBuffer str) {
        if(str==null){
            return null;
        }
       StringBuilder newStr = new StringBuilder();
        for(int i=0;i<str.length();i++){
            if(str.charAt(i)==' '){
                newStr.append('%');
                newStr.append('2');
                newStr.append('0');
            }else{
                newStr.append(str.charAt(i));
            }
        }
        return newStr.toString();
    }
}

3.输入一个链表,从尾到头打印链表每个节点的值。
思路:使用一个栈来存储链表值。

public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        Stack<Integer> stack = new Stack<>();
        while (listNode != null) {
            stack.push(listNode.val);
            listNode = listNode.next;
        }

        ArrayList<Integer> list = new ArrayList<>();
        while (!stack.isEmpty()) {
            list.add(stack.pop());
        }
        return list;       
    }
}

4.输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) 
    {
        TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
        return root;
    }
    //前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
    private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {

        if(startPre>endPre||startIn>endIn)
            return null;
        TreeNode root=new TreeNode(pre[startPre]);

        for(int i=startIn;i<=endIn;i++)
            if(in[i]==pre[startPre]){
                root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
                root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn);
                      break;
            }

        return root;
    }
}


  class TreeNode {
     int val;
     TreeNode left;
     TreeNode right;
     TreeNode(int x) { val = x; }
  }

5 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    public void push(int node) {
        stack1.push(node);
    }

    public int pop() {
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
        int first=stack2.pop();
        while(!stack2.isEmpty()){
            stack1.push(stack2.pop());
        }
        return first;
    }
}

6.把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0

思路:二分查找

public static int search(int arr[],int key) 
    {
        int left=0;
        int right=arr.length-1;
        while(left<=right)
        {
            int mid=(left+right)/2;
            if(arr[mid]==key)
                return mid;
            if(arr[mid]<key)
                left=mid+1;
            else
                right=mid-1;
        }
        return -1;
    }

7.现在要求输入一个整数n,请你输出斐波那契数列的第n项。

class Solution {
public int Fibonacci(int n) 
{
        int f = 0, g = 1;
        while(n>0) {
            g += f;
            f = g - f;
        }
        return f;
    }
};

8.一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

public class Solution {
    public int JumpFloor(int target) {
        if(target <= 0) return 0;
        if(target == 1) return 1;
        if(target == 2) return 2;
        int one = 1;
        int two = 2;
        int result = 0;
        for(int i = 2; i < target; i++){
            result = one+ two;
            one = two;
            two = result;
        }
        return result;
    }
}

9.一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

思路:
关于本题,前提是n个台阶会有一次n阶的跳法。分析如下:
f(1) = 1
f(2) = f(2-1) + f(2-2) //f(2-2) 表示2阶一次跳2阶的次数。
f(3) = f(3-1) + f(3-2) + f(3-3)

f(n) = f(n-1) + f(n-2) + f(n-3) + … + f(n-(n-1)) + f(n-n)

说明:
1)这里的f(n) 代表的是n个台阶有一次1,2,…n阶的 跳法数。
2)n = 1时,只有1种跳法,f(1) = 1
3) n = 2时,会有两个跳得方式,一次1阶或者2阶,这回归到了问题(1) ,f(2) = f(2-1) + f(2-2)
4) n = 3时,会有三种跳得方式,1阶、2阶、3阶,
那么就是第一次跳出1阶后面剩下:f(3-1);第一次跳出2阶,剩下f(3-2);第一次3阶,那么剩下f(3-3)
因此结论是f(3) = f(3-1)+f(3-2)+f(3-3)
5) n = n时,会有n中跳的方式,1阶、2阶…n阶,得出结论:
f(n) = f(n-1)+f(n-2)+…+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + … + f(n-1)
6) 由以上已经是一种结论,但是为了简单,我们可以继续简化:
f(n-1) = f(0) + f(1)+f(2)+f(3) + … + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + … + f(n-2)
f(n) = f(0) + f(1) + f(2) + f(3) + … + f(n-2) + f(n-1) = f(n-1) + f(n-1)
可以得出:
f(n) = 2*f(n-1)
7) 得出最终结论,在n阶台阶,一次有1、2、…n阶的跳的方式时,总得跳法为:
f(n) = 1 ,(n=0 )
f(n) = 1 ,(n=1 )
f(n) = 2*f(n-1) ,(n>=2)

public class Solution {
    public int JumpFloorII(int target) {
        if (target <= 0) {
            return -1;
        } else if (target == 1) {
            return 1;
        } else {
            return 2 * JumpFloorII(target - 1);
        }
    }
}

10.输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while(n!= 0){
            count++;
            n = n & (n - 1);
         }
        return count;
    }
}

11.输入一个链表,输出该链表中倒数第k个结点。

思路:两个指针,先让第一个指针和第二个指针都指向头结点,然后再让第一个指正走(k-1)步,到达第k个节点。然后两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了。

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

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null||k<=0){
            return null;
        }
        ListNode pre=head;
        ListNode last=head;       
        for(int i=1;i<k;i++){
            if(pre.next!=null){
                pre=pre.next;
            }else{
                return null;
            }
        }
        while(pre.next!=null){
            pre = pre.next;
            last=last.next;
        }
        return last;
    }
}

12.输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

public class Solution {
public static void reOrderArray(int [] array) 
    {
           for(int i= 0;i<array.length-1;i++)
           {
                for(int j=0;j<array.length-1-i;j++)
                {
                    if(array[j]%2==0&&array[j+1]%2==1)
                    {
                        int t = array[j];
                        array[j]=array[j+1];
                        array[j+1]=t;
                    }
                }
            }
     }
}

13.输入一个链表,反转链表后,输出链表的所有元素。

/r1,r2分别为自己写的递归与非递归方法
        public static Node r1(Node head)
        {
            if(head==null||head.getNext()==null)
                return head;

            Node finall=r1(head.getNext());
            head.getNext().setNext(head);
            head.setNext(null);
            return finall;
        }

        public static Node r2(Node head)
        {
            if(head==null||head.getNext()==null)
                return head;
            Node temp=null;
            Node pre=null;
            Node cur=head;

            while(cur!=null)
            {
                temp=cur.getNext();
                cur.setNext(pre);
                pre=cur;
                cur=temp;
            }
            head.setNext(null);
            return pre;
        }

14.输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

【思路】借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。

举例:

入栈1,2,3,4,5
出栈4,5,3,2,1
首先1入辅助栈,此时栈顶1≠4,继续入栈2
此时栈顶2≠4,继续入栈3
此时栈顶3≠4,继续入栈4
此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,,辅助栈里面是1,2,3
此时栈顶3≠5,继续入栈5
此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3
….
依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。

import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        if(pushA.length == 0 || popA.length == 0)
            return false;
        Stack<Integer> s = new Stack<Integer>();
        //用于标识弹出序列的位置
        int popIndex = 0;
        for(int i = 0; i< pushA.length;i++){
            s.push(pushA[i]);
            //如果栈不为空,且栈顶元素等于弹出序列
            while(!s.empty() &&s.peek() == popA[popIndex]){
                //出栈
                s.pop();
                //弹出序列向后一位
                popIndex++;
            }
        }
        return s.empty();
    }
}

15.输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

思路:BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。完美的递归定义。

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
   int count=sequence.length;
        if(count==0)
            return false;
        return isRight(sequence,0,count-1);

    }

    public boolean isRight(int[] sequence,int start,int end){
        if(start>=end) return true;
        int i=end-1;
        while(sequence[i]>sequence[end]&&i>start) i--;
          for(int j=start;j<i;j++){
              if(sequence[j]>sequence[end])
                  return false;
          }
        return isRight(sequence,start,i)&&isRight(sequence,i+1,end-1);


    }
}

16.输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
        private ArrayList<Integer> list = new ArrayList<Integer>();
        public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) 
        {
                if(root == null) 
                    return listAll;

                list.add(root.val);
                target -= root.val;

                if(target == 0 && root.left == null && root.right == null)
                    listAll.add(new ArrayList<Integer>(list));

                FindPath(root.left, target);
                FindPath(root.right, target);               
                list.remove(list.size()-1);
                return listAll;
        }

17.输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

public class Solution {
    public static void main(String[] args) {
        Solution p = new Solution();
        System.out.println(p.Permutation("abc").toString());
    }

    public ArrayList<String> Permutation(String str) {
        List<String> res = new ArrayList<>();
        if (str != null && str.length() > 0) {
            PermutationHelper(str.toCharArray(), 0, res);
            Collections.sort(res);
        }
        return (ArrayList)res;
    }

    public void PermutationHelper(char[] cs, int i, List<String> list) {
        if (i == cs.length - 1) {
            String val = String.valueOf(cs);
            if (!list.contains(val))
                list.add(val);
        } else {
            for (int j = i; j < cs.length; j++) {
                swap(cs, i, j);
                PermutationHelper(cs, i+1, list);
                swap(cs, i, j);
            }
        }
    }

    public void swap(char[] cs, int i, int j) {
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }
}

18.数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

思路:数组排序后,如果符合条件的数存在,则一定是数组中间那个数。

public int MoreThanHalfNum_Solution(int [] array) {
        int len=array.length;
        if(len<1){
            return 0;
        }
        int count=0;
        Arrays.sort(array);
        int num=array[len/2];
        for(int i=0;i<len;i++){
            if(num==array[i])
                count++;
        }
        if(count<=(len/2)){
            num=0;
        }
        return num;
}

20.输入n个整数,找出其中最小的K个数

思路:基于堆排序算法,构建最大堆。时间复杂度为O(nlogk);
如果用快速排序,时间复杂度为O(nlogn);
改造的堆排序:
1.将前k个元素构造成最大堆(0~k-1)
2.从k位开始,依次与最大堆的堆顶元素进行比较。如果比堆顶小,则该数与堆顶交换位置,维护最大堆
3.最后从k-1~0的数是最小的k的数

import java.util.*;
public class Solution {
    public ArrayList<Integer>GetLeastNumbers_Solution(int[] input, int k) {
        ArrayList<Integer> array = new ArrayList<Integer>();
        if(input==null||input.length==0||k<=0||k>input.length){
            return array;
        }
        for(int i=k/2-1;i>=0;i--){
            buildMaxHeapSort(input,i,k);
        }
        for(int j=k;j<input.length;j++){
            if(input[j]<input[0]){
                swap(input,0,j);
                buildMaxHeapSort(input,0,k);
            }
        }
        for(int i=k-1;i>=0;i--){
            array.add(input[i]);
        }
        return array;
    }

    public void buildMaxHeapSort(int[] input,int i,int k){
        int leftchild=2*i;
        int rightchild=2*i+1;
        int larget=i;
        if(leftchild<k&&input[i]<input[leftchild]){
            larget=leftchild;
        }
        if(rightchild<k&&input[larget]<input[rightchild]){
            larget=rightchild;
        }
        if(larget!=i){
            swap(input,i,larget);
            buildMaxHeapSort(input,larget,k);
        }
    }

    public void swap(int[] input,int a,int b){
        int temp=input[a];
        input[a]=input[b];
        input[b]=temp;
    }
}

21.求出任意非负整数区间中1出现的次数。如:算出100~1300的整数中1出现的次数?

public int NumberOf1Between1AndN_Solution(int n) {
        int count=0;
        while(n>0){
            String str=String.valueOf(n);
            char [] chars=str.toCharArray();
            for(int i=0;i<chars.length;i++){
                if(chars[i]=='1')
                    count++;
            }
            n--;
        }
        return count;
}

22.把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

public class Solution {
    public int GetUglyNumber_Solution(int index) {    
        if(index<=0)
            return 0;
        int[] result = new int[index];
        int count = 0;
        int i2 = 0;
        int i3 = 0;
        int i5 = 0;

        result[0] = 1;
        int tmp = 0;
        while (count < index-1) {
            tmp = min(result[i2] * 2, min(result[i3] * 3, result[i5] * 5));
            if(tmp==result[i2] * 2) i2++;//三条if防止值是一样的,不要改成else的
            if(tmp==result[i3] * 3) i3++;
            if(tmp==result[i5]*5) i5++;
            result[++count]=tmp;
        }
        return result[index - 1];
    }

    private int min(int a, int b) {
        return (a > b) ? b : a;
    }
}

23.在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置

public int FirstNotRepeatingChar(String str) 
    {
        if (str.length()==0)
                return 0;

        char c[]=str.toCharArray();
        ArrayList<Character> a=new ArrayList<Character>();
        ArrayList<Character> b=new ArrayList<Character>();

        for(int i=0;i<c.length;i++)
        {
            if(!a.contains(c[i])&&!b.contains(c[i]))
                a.add(c[i]);
            else
                if(a.contains(c[i]))
                {
                    a.remove((Character)c[i]);
                    b.add(c[i]);
                }
        }
        return str.indexOf(a.get(0));
    }

24.给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

思路:
分析二叉树的下一个节点,一共有以下情况:
1.二叉树为空,则返回空;
2.节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点;
3.节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。

public class Solution
{
    public TreeLinkNode GetNext(TreeLinkNode node)
    {
        if(node==null)return null;
        if(node.right!=null)
        {
            node=node.right;
            while(node.left!=null)
            {
                node=node.left;

            }return node;
        }
        while(node.next!=null)
        {
            if(node.next.left==node)return node.next;
            node=node.next;
        }
        return null;
    }
}

25.给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。

思路:二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序。所以,按照中序遍历顺序找到第k个结点就是结果。

//中序遍历  
    public void inorder(BinTree root){  
        if(root!=null){  
            inorder(root.lChild);  
            list.add(root.getData());  
            inorder(root.rChild);  
        }  

}  

常用算法1:二叉搜索

public static int search(int arr[],int key) 
    {
        int left=0;
        int right=arr.length-1;
        while(left<=right)
        {
            int mid=(left+right)/2;
            if(arr[mid]==key)
                return mid;
            if(arr[mid]<key)
                left=mid+1;
            else
                right=mid-1;
        }
        return -1;
    }

常用算法2:二叉树的创建及遍历

public void createTree(Object[] objs){  
        datas=new ArrayList<BinTree>();

        for (Object object : objs) 
        {  
            datas.add(new BinTree(object));  
        }  

        root=datas.get(0);//将第一个作为根节点  

        for (int i = 0; i < objs.length/2; i++) 
        {  
            datas.get(i).lChild=datas.get(i*2+1);  
            if(i*2+2<datas.size())
            {
                //避免偶数的时候 下标越界  
                datas.get(i).rChild=datas.get(i*2+2);  
            }  
        }  
    }  
    //先序遍历  
    public void preorder(BinTree root){  
        if(root!=null){  
            visit(root.getData());  
            preorder(root.lChild);  
            preorder(root.rChild);  
        }  

    }  
    //中序遍历  
    public void inorder(BinTree root){  
        if(root!=null){  
            inorder(root.lChild);  
            visit(root.getData());  
            inorder(root.rChild);  
        }  

    }  
    //后序遍历  
    public void afterorder(BinTree root){  
        if(root!=null){  
            afterorder(root.lChild);  
            afterorder(root.rChild);  
            visit(root.getData());  
        }  

    }  
    private void visit(Object obj) {  
        System.out.print(obj+" ");  
    }  
    public Object getData() {  
        return data;  
    }  
    public BinTree getRoot() {  
        return root;  
}  

常用算法3:反转链表

//r1,r2分别为自己写的递归与非递归方法
        public static Node r1(Node head)
        {
            if(head==null||head.getNext()==null)
                return head;

            Node finall=r1(head.getNext());
            head.getNext().setNext(head);
            head.setNext(null);
            return finall;
        }

        public static Node r2(Node head)
        {
            if(head==null||head.getNext()==null)
                return head;
            Node temp=null;
            Node pre=null;
            Node cur=head;

            while(cur!=null)
            {
                temp=cur.getNext();
                cur.setNext(pre);
                pre=cur;
                cur=temp;
            }
            head.setNext(null);
            return pre;
        }

常用算法4:快速排序:

public void quick_sort(int s[], int l, int r)  
    {  
        if (l < r)  
        {  
            int i = l, j = r, x = s[l];  
            while (i < j)  
            {  
                while(i < j && s[j] >= x) // 从右向左找第一个小于x的数  
                    j--;    
                if(i < j)   
                {
                    s[i] = s[j]; 
                    i++;
                }
                while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数  
                    i++;    
                if(i < j)
                {
                    s[j] = s[i]; 
                    j--;
                }
            }  
            s[i] = x;  
            quick_sort(s, l, i - 1); // 递归调用   
            quick_sort(s, i + 1, r);  
        }  
    }