刷题找工作第7篇

刷题找工资第7篇 – 剑指offer

第一题

第一次出现一次的字符
题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).

import java.util.LinkedHashMap;

public class Solution {
    public int FirstNotRepeatingChar(String str) {
        
        char[] p = str.toCharArray();
        LinkedHashMap<Character,Integer> check = new LinkedHashMap();
        int pos = -1;
        for(char i:p)
        {
            int count = check.getOrDefault(i,0);
            check.put(i,++count);

        }
        for(char i:p)
        {
            if(check.get(i)==1)
                return ++pos;
            ++pos;
        }
        return pos;
            

    }
}

这个题开始想到时TreeMap,后来用HashMap ,没想到题目问的是位置
可以用TreeMap或者是 LinkedHashMap,因为都是有序的表,下面是两者的运行时间
在这里插入图片描述
在这里插入图片描述
这是网上关于 TreeMap 和 LinkedHashMap 的一篇文章
各种Map,TreeMap添加删除都是 log(N),因为是 红黑树实现,log(N) 的时间调整树结构,类似于堆,而LinkedHashMap 是一个双向链表,双向链表应该要快

还有一种做法,是看评论C++写的,也用java写了一下,这个indexOf 听说是kmp 算法实现的,当然那是字符串匹配,这个只是查找字符,str中每个位置的字符,我从左边开始找,和从右边开始找,如果这个两个方向找到的位置是一样的,那说明这个字符在字符串中只出现一次。
下面是运行时间和代码
在这里插入图片描述
还是挺快的,时间复杂度没分析错应该是 o(N^2)

import java.util.LinkedHashMap;

public class Solution {
    public int FirstNotRepeatingChar(String str) {
        int len = str.length();
        for(int i=0;i<len;++i)
        {
            char p = str.charAt(i);
            if(str.indexOf(p)==str.lastIndexOf(p)) 
                return i;
        }
        return -1;

    }
}

第二题

数组中的逆序对
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

举个例子: 1 2 3 4 5 0
由于 1>0, 2>0,3>0…5>0 ,有5个大于0,那么返回 5
题目问的是求 逆序对的对数
思路分析:
直接暴力算法,枚举i,枚举j,双循环枚举每个数
方法二: 归并排序
举个例子,我把一个数组分成两半,左边有序了,右边也排好序了,就要开始合并两个数组了
【1,3,5,6,7,-4,-2,0,2】
我们看到 arr[mid] = 7,从【-4,-2,0】都比 1要小,逆序对为3,注意:后面的3,5,6,7也一定比【-4,-2,0】要大,因此 arr[l] 到arr[mid]中的每一个数都比这个序列要大
//a[i]>a[j]了,那么这一次,从a[i]开始到a[mid]必定都是大于这个a[j]的,因为此时分治的两边已经是各自有序了

public class Solution {
    private int P;
    public int InversePairs(int [] arr) {
        this.P=0;
        mergeSort(arr,0,arr.length-1);
        
        return P;
        
       
    }
    public void mergeSort(int[] arr,int l,int r) {
        if(r<=l) return;
        int m = (l+r)>>>1;
        mergeSort(arr,l,m);
        mergeSort(arr,m+1,r);
        merge(arr,l,m,r);

    }
    public void merge(int[] arr,int l,int m,int r)
    {
        int[] temp = new int[r-l+1];
        int leftEnd=m++,rightEnd=r;
        r=0;
        
        while(l<=leftEnd&&m<=rightEnd)
        {
            if(arr[l]<arr[m]) temp[r++]=arr[l++];
            else {
                P+=(leftEnd-l+1);
                if(P>1000000007) P%=1000000007;
                temp[r++]=arr[m++];
            }
        }
        while(l<=leftEnd) temp[r++]=arr[l++];
        while(m<=rightEnd) temp[r++]=arr[m++];
        do{
            arr[rightEnd--]=temp[--r];
        }while(r>0);

        
    }
}

题目3

两个链表的第一个公共节点
题目描述
输入两个链表,找出它们的第一个公共结点。
时间复杂度 O(N) ,空间复杂度 O(N)
在这里插入图片描述

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

    ListNode(int val) {
        this.val = val;
    }
}*/
import java.util.HashSet;
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1==null||pHead2==null) return null;
         if(pHead1.next==pHead2) return pHead2;
        if(pHead2.next==pHead1) return pHead2;
        
         HashSet<ListNode> set = new HashSet();
        ListNode begin=pHead1;
         while(begin!=null)
         {
             set.add(begin);
             begin=begin.next;
         }
        begin = pHead2;
        while(begin!=null)
        {
            if(set.contains(begin)) return begin;
            begin= begin.next;
        }
        return null;
    }
}

我看到评论区域还有一种奇妙的做法 ,
举个例子 链表 1长度是 m,链表2长度是 n ,那么 假设 n> m,且 n-m =d,长链表要比短链表多走d步
两个链表从起点开始, 1是短链表,到达终点了就又跑回起点
也就是说,短链表要循环 d轮才能和长链表在同一起跑线上,并且有可能相遇,如果后面同时指向null了,就退出循环,返回null
但是,如果链表成环了怎么办?貌似这个方法不行
还有是用栈的方法,就不写了


public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1==null||pHead2==null) return null;
        ListNode l = pHead1,r= pHead2;
        while(l!=r)
        {
            l = l==null ? pHead2:l.next;
            r = r==null ? pHead1:r.next;
            
        }
        return l;
    }
}

第4题

数字在排序数组出现的次数
题目描述
统计一个数字在排序数组中出现的次数。

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       int ans = 0;
        for(int i:array)
        {
            if(i==k) ++ans;
        }
        return ans;
    }
}

我看到这个题就在想。。怎么这么水。。 尴尬

import java.util.Arrays;
public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       int ans = 0;
        if(array.length==1) return ans= array[0]==k ? 1:0;
       int i = Arrays.binarySearch(array,k);
        if(i>0){
            int l = i-1,r=i+1;
            while(l>=0)
            {
                if(array[l--]==k) ans++;
            }
            while(r<array.length)
            {
                if(array[r++]==k) ans++;
            }
            return ans+1;
            
        }
        return ans;
    }
}

题目并没有说数组是有序的,如果是有序的只需要 logN 的时间找到这个k,并且如果 k序列长度为 m
只需要 O(m*logN)
但是,如果数组是无序的,使用二分法要排序,时间复杂度是 O(N *logN) ,还不如 遍历

第五题

二叉树的最大深度
题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

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

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root==null) return 0;
        
        return dfs(root);
    }
    
    public int dfs(TreeNode root)
    {
        if(root==null) return 0;
        
        int left = dfs(root.left);
        int right = dfs(root.right);
        return Math.max(left,right)+1;
    }
    
    
    
    
    
    
}

层序遍历一颗二叉树,要求其最大深度,要记住当前层的总的节点个数,当出队的节点个数等于当前层总节点个数了,就数下一次节点个数,开始了下一层的遍历

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

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
import java.util.Queue;
import java.util.LinkedList;
public class Solution {
    public int TreeDepth(TreeNode root) {
        int d=0;
        if(root==null) return d;
        Queue<TreeNode> q = new LinkedList();
        q.offer(root);
        int curCount = q.size(),count=0;
        while(curCount!=0)
        {
            TreeNode top = q.poll();
            ++count;
            if(top.left!=null) q.offer(top.left);
            if(top.right!=null) q.offer(top.right);
            if(count==curCount)
            {
                ++d;
                count=0;
                curCount= q.size();
            }
        }
        return d;
    }
    

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值