面试算法题目

将最近遇到的一些难题整理一下,以供参考,持续更ing~

目录

数据结构

       字母分离,小写在前,大写在后,顺序不变,空间复杂度为O(1)

       求逆序对(递归调用,O(n*logn))

        二叉树前、中、后非递归遍历

 

最大子数组和(滴滴)

最小两个连续子数组和

最长重复子串(阿里)

加密解密(头条)

分糖果(头条)

最长无重复字符子串(头条)

求取货币兑换方式(平安科技)

约瑟夫环问题

快排(两种方式)

电话号码=》单词

二叉树非递归前中后序遍历


数据结构

       双指针处理两数相加之和,时间复杂度O(n)

    def twoSum(self, numbers, target):
        """
        167. 两数之和 II - 输入有序数组
        给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
        函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
        说明:
        返回的下标值(index1 和 index2)不是从零开始的。
        你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
        输入: numbers = [2, 7, 11, 15], target = 9
        输出: [1,2]
        解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

        思路:利用双指针,左右递进,可实现O(n)时间复杂度,类似于快排
        :param numbers:
        :param target:
        :return:
        """
        l = 0
        r = len(numbers) - 1
        while l < r:
            if numbers[l] + numbers[r] == target:
                return [l+1, r+1]
            elif numbers[l] + numbers[r] < target:
                l += 1
            else:
                r -= 1
        return []

       字母分离,小写在前,大写在后,顺序不变,空间复杂度为O(1)

    def Resort(self, string):
        if len(string) < 2:
            return string
        arr = list(string)
        n = 0
        for i in arr:
            if i>='A'and i<='Z':
                n += 1
        i = 0
        flag = True
        while i < n and flag:
            flag = False
            for j in range(0,len(arr)-i-1):
                if arr[j+1]>='a' and arr[j+1]<='z'and arr[j]>='A'and arr[j]<='Z':
                    k = arr[j]
                    arr[j] = arr[j+1]
                    arr[j+1] = k
                    flag = True
            i += 1
        return ''.join(arr)

       求逆序对(递归调用,O(n*logn))

#coding:utf-8
from functools import cmp_to_key

class Solution():
    def __init__(self):
        self.count = 0

    def Partition(self, left, right):
        l = 0
        r = 0
        result = []
        while l<len(left) and r<len(right):
            if left[l] < right[r]:
                result.append(left[l])
                l += 1
            else:
                self.count += (len(left)-l)
                result.append(right[r])
                r += 1
        result += left[l:]
        result += right[r:]
        return result
    def Merge(self, array):
        if len(array) == 1:
            return array

        m = int(len(array)/2)
        left = self.Merge(array[:m])
        right = self.Merge(array[m:])
        return self.Partition(left, right)





if __name__ == "__main__":
    sl = Solution()
    nums = [1,2,3,4,5,6,7,0]
    print(sl.Merge(nums))
    print(sl.count)

 

        二叉树前、中、后非递归遍历

    def ProOrder(root):
        ans = []
        stack = []
        while root or stack:
            while root:
                ans.append(root.val)
                stack.append(root)
                root = root.left
            if stack:
                root = stack.pop()
                root = root.right
        return ans

    def MidOrder(root):
        ans = []
        stack = []
        while root or stack:
            while root:
                stack.append(root)
                root = root.left
            if stack:
                root = stack.pop()
                ans.append(root.val)
                root = root.right
        return ans

    def PostOrder(root):
        ans = []
        stack = []
        while root or stack:
            while root:
                ans.append(root.val)
                stack.append(root)
                root = root.right
            if stack:
                root = stack.pop()
                root = root.left
        return ans[::-1]

 

最大子数组和(滴滴)

    def maxSubArray(self,array):
        """
        描述:输入一个整数数组,返回最大连续子数组和  
              输入 [1, -2, 3, -4, 5, 6, -7]
              输出   11
        :param array: 
        :return:max
        """
        max, cur = -0xffff, 0
        for i in range(len(array)):
            if cur > 0:
                cur += array[i]
            else:
                cur = array[i]
            if(max<cur):
                max = cur
        return max

最小两个连续子数组和

#coding:__utf-8__
def min_shulie(lists):
    """
    描述:给定了一个序列,找出其中两组排列最小的连续序列之和相加
    :param lists:
    :return:
    """
    Min, cur =0xffff, 0
    for i in lists:
        if cur >= 0:
            cur = i
        else:
            cur += i

        if cur < Min:
            Min = cur
    return Min
def liangxulie(lists):
    res = []
    buf = []
    for i in range(1,len(lists)-1):
        if lists[i] > 0:
            buf.append(min_shulie(lists[:i]))
            buf.append(min_shulie(lists[i+1:]))
            res.append(buf[0]+buf[1])
            buf = []
    res.sort()
    print(res[0])
if __name__ == "__main__":
    lists = [2,-2,1,-3,3,-9,5,-5,-6,-8]
    liangxulie(lists)

最长重复子串(阿里)

def maxRepat(self,String):
    """
    描述:输入一个字符串,求出最长重复子串
    输入:"hello, my name is chenchi. is chenchi."
    输出:" is chenchi.",注:空格也要算。
    :param String:
    :return:
    """
    if not String or len(String) == 0:
        return None
    String = list(String)
    max, count = -0xffff, 0
    for i in range(1,len(String)):
        for j in range(len(String)-i):
            if String[i+j] == String[j]:
                count += 1
            else:
                count = 0
            if max < count:
                max = count
                first = j - count +1
    return ''.join(String[first:first+max+1])

加密解密(头条)

    def jiami(self):
        #滑窗法求解
        # N, K = map(int,input().split(' '))
        N, K = 7, 4
        secret = "1110100110"
        #result = "1001010"
        res = [int(secret[0])]
        for i in range(1,N-1):
            pre = int(secret[i-1])
            now = int(secret[i])
            val = pre^now
            if i >=K:
                val ^= res[i-K]
            res.append(val)
        res.append(secret[-1])
        print(''.join([str(x)for x in res]))
        return
        #加密
        # for i in range(K):
        #     res.append('0b'+string+'0'*i)
        # result = int(res[0],2)
        # # print(result)
        # for j in range(1,K):
        #     result = result^(int(res[j],2))
        # print(bin(result).replace('0b', ''))
        # return

 

分糖果(头条)

#(贪心算法)
#描述:要求对于每个学生获得得分数,所得糖果左右分高者要得的糖果比分低者多 
   def candy(selfs):
        arr = [1,0,2]
        nums = [1]*len(arr)
        for i in range(len(arr)-1):
            if arr[i] < arr[i+1]:
                nums[i+1] += 1
        for i in range(len(arr)-1,0,-1):
            if arr[i] < arr[i-1]:
                nums[i-1] = max(nums[i]+1, nums[i-1])
        print(sum(nums))
        return

最长无重复字符子串(头条)

    def maxUnrepat(self,string):
        lists = list(string)
        if not len(lists):
            return 0
        res = []  # 每一次的长度
        buf = []  # 存取总共的不重复长度
        start = 0
        for i in range(len(lists)):
            if lists[i] not in res:
                res.append(lists[i])
            else:
                start = res.index(lists[i])
                buf.append(res)
                res = res[start + 1:]
                res.append(lists[i])
        buf.append(res)
        max_len = len(buf[0])
        max_string = buf[0]
        for i in buf:
            if max_len < len(i):
                max_len = len(i)
                max_string = i
        return ''.join(max_string)

求取货币兑换方式(平安科技)

输入 n=100,a1=0,a2=0,a3=5,a4=6,a5=3,a6=2

输出 8

package com.chenyao.demo;
import java.util.*;
import java.util.concurrent.CountDownLatch;

public class money_distribute {
/**
 * input:n=100,a1=0,a2=0,a3=5,a4=6,a5=3,a6=2
 * output:8
 * */
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        String s1 = input.nextLine();  //识别到回车作为结束,行输入(next()为字符输入识别,一旦不是字符就停止)
        String[] s2 = s1.split("\\=|,");//正则表达式实现 分割操作
        int n = Integer.parseInt(s2[1]);
        int[] num = new int[6];
        for(int i = 0;i<6;i++){
            num[i] = Integer.parseInt(s2[3+i*2]); //字符串转整型
        }
        int[] moneyValue = {1,5,10,20,50,100};
        int m = num.length-1;
        int count = 0;
        count = Count_money_distribute(n,num,m,moneyValue);
        System.out.println(count);
    }
    private static int Count_money_distribute(int n,int[] num, int m, int[] moneyValue){
        if((n<0)||(m<0))
            return 0;
        if (n==0)
            return 1;
        int count = 0;
        for(int i=0;i<=num[m];i++)  //递归通过逐渐降低n,获得兑换种类
            count = count + Count_money_distribute(n-i*moneyValue[m],num,m-1,moneyValue);
        return count;
    }

}

约瑟夫环问题

/********
 * 描述:约瑟夫环问题,让一群小朋友围成一个圈,从一个小朋友按1,2,3,、、、标号,
 * 让他们报数,doom=3,就按照1,2,3报数,喊到doom值该人出圈,从下一个继续从1报数,知道最后剩下一个人
 * 输入:count:小朋友总人数,doom:去人间隔
 * 输出:剩下人的标号
 * *******/
#环形链表法,本程序为变种,从第一个开始杀死,找到最后的幸存者,
#
def josephus(n, k):
    if k==1:
        print('survive:', n)
        return
    alive = n
    preIndex, curIndex = 0, 1
    List = [(i+1)%alive for i in range(n)] #储存下一个地址的位置
    List[-1] = 1  #先把最后一个地址的位置制成第二个数字位置
    alive -= 1 #总数减掉1
    while alive > 0:
        num = k%alive - 1 #计算下一个的偏移量,通常会考虑是否大于k的情况
        if num == -1:
            length = alive - 1
        else:
            length = num
        for index in range(0, length):
            preIndex = curIndex
            curIndex = List[curIndex]

        print(curIndex+1)
        alive -= 1
        List[preIndex] = List[curIndex] #该处指向了下下个点位置
        curIndex = List[curIndex]
    return
    private static int joseph(int count,int doom){
        /********
         * 描述:约瑟夫环问题,让一群小朋友围成一个圈,从一个小朋友按1,2,3,、、、标号,
         * 让他们报数,doom=3,就按照1,2,3报数,喊到doom值该人出圈,从下一个继续从1报数,知道最后剩下一个人
         * 输入:count:小朋友总人数,doom:去人间隔
         * 输出:剩下人的标号
         * *******/

        int alive = count;              //幸存的人数
        int[] List = new int[count];    //构建链表,当前各自储存下一个点值
        int preIndex = 0,curIndex = 0;
        for (int i=0;i<count;i++){
            List[i] = (i+1)%alive;
        }
        while(alive>0){
            int num = doom%alive - 1;   //直接计算出移动的人数
            for(int index=0;index<(num == -1?alive -1:num);index++){
                preIndex = curIndex;
                curIndex = List[curIndex];
            }
            //计算出当前出去人的位置
            if(alive == 1)
                System.out.print("  "+(curIndex+1)+"  " );
            else
                System.out.print("  "+(curIndex+1)+"  " );
            alive --;
            //出圈操作,就是让链表下一个储存位置更新
            List[preIndex] = List[curIndex];
            curIndex = List[curIndex];
        }
        return List[curIndex];
    }

字符串匹配

KMP算法,查找字符串T在字符串S中所在起始位置

 private static void get_next(char[] T,int[] next){
        int i,j;
        i = 1;
        j = 0;
        next[1] = 0;
        while(i < T[0]){
            /*T[i]表示后缀的单个字符*/
            /*T[j]表示前缀的单个字符*/
            if(j == 0 || T[i] == T[j]){
                ++i;
                ++j;
                if(T[i] == T[j])
                    next[i] = next[j];
                else
                    next[i] = j;
            }
            else
                j = next[j];
        }
    }
    private static int Index_KMP(char[] S,char[] T,int pos){
        int i = pos;
        int j = 1;
        int[] next = new int[10];
        get_next(T,next);
        while(i <= S[0] && j <= T[0]){
            if(j == 0 || S[i] == T[j]){
                ++i;
                ++j;
            }
            else {
                j = next[j];
            }
        }
        if(j > T[0])
            return (i-T[0]);
        else
            return 0;
    }

堆排序

讲解:https://www.jianshu.com/p/d174f1862601

from collections import deque
def swap(L, i, j):
    L[i], L[j] = L[j], L[i]
    return L

def heap_adjust(L, start, end):
    temp = L[start]

    i = start
    j = 2 * i

    while j <= end:
        if j < end and L[j] < L[j+1]:
            j += 1
        if temp < L[j]:
            L[i] = L[j]
            i = j
            j = 2 * i
        else:
            break
    L[i] = temp


def heap_sort(L):
    L_length = len(L) - 1
    first_sort_count = L_length // 2
    for i in range(first_sort_count):
        heap_adjust(L, first_sort_count-i, L_length)

    for i in range(L_length - 1):
        L = swap(L, 1, L_length - i)
        heap_adjust(L, 1, L_length - i - 1)

    return [L[i] for i in range(1,len(L))]

if __name__ == "__main__":
    L = deque([50, 16, 30, 10, 60,  90,  2, 80, 70])
    L.appendleft(0)
    print(heap_sort(L))

快排(两种方式)

    def swap(self,lists,low,high):
        if low==high:
            return
        k=lists[low]
        lists[low]=lists[high]
        lists[high]=k
        return
    def Partition(self,lists,low,high):
        """
        采用二分递归的Partition
        1.选择主元素(一般是数组末尾元素)
        2.定义2个index:i记录不大于主元素的个数;j当前遍历值
        以主元素为基准,小于主元素的位于主元素左侧,反之位于主元素右侧
        :param lists:
        :param low:
        :param high:
        :return:
        """
        i = low
        for j in range(low,high):
            if lists[j] <= lists[high]:
                sl.swap(lists,j,i)
                i+=1
        sl.swap(lists,i,high)
        return i
        """
        该种方式:通过先从底下遍历,遇到大的再从上边遍历,左右交换,知道左右碰头,再跟总值交换
        """
        # pivot = high
        # pivotKey = lists[high]
        # while low<high:
        #     #从左向右遍历
        #     while low<high and lists[low]<=pivotKey:
        #             low += 1
        #     print("low:",low,"high:",high,"pivot:",pivot,"左边遍历:》》",lists)
        #     while low<high and lists[high]>=pivotKey:
        #             high -= 1
        #     print("low:",low,"high:",high,"pivot:",pivot,"右边遍历:》》",lists)
        #     sl.swap(lists, low, high)
        #     print("low:",low,"high:",high,"pivot:",pivot,"交换后:》",lists)
        # sl.swap(lists,low,pivot)
        # print("low:",low,"high:",high,"pivot:",pivot,"结束一轮:>>",lists)
        # return low



     def Quick_sort(self,lists,low,high):
        if len(lists) <= 1:
            return lists
        if low<high:
            pivot=sl.Partition(lists,low,high)
            sl.Quick_sort(lists,low,pivot-1)
            sl.Quick_sort(lists,pivot+1,high)
        return lists

电话号码=》单词

    def getoutput(self,Input):
        n = len(Input)
        M = {
            '2':['A','B','C' ],
            '3':['D','E','F'],
            '4':['G','H','I'],
            '5':['J','K','L'],
            '6':['M','N','O'],
            '7':['P','Q','R','S'],
            '8':['T','U','V'],
            '9':['W','X','Y','Z']
             }
        Buf = []
        res = []
        def partition(k):
            if k == n:
                Buf.append(copy.deepcopy(res))
                return
            for i in M[Input[k]]:
                res.append(i)
                partition(k+1)
                res.pop(-1)
        partition(0)
        print(Buf)
        return

二叉树非递归前中后序遍历

# -*- 二叉树 begin -*-
# 前序遍历:根结点->左子树->右子树
# 中序遍历:左子树->根结点->右子树
# 后序遍历:左子树->右子树->根结点


class Node(object):
    """节点类"""

    def __init__(self, value=None, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right


class Tree(object):
    """定义二叉树类"""

    def __init__(self):
        self.root = Node()
        self.nodeList = []  # 用以存放相对的父节点,用以开辟其子节点

    def add(self, value):
        """添加节点"""
        node = Node(value)
        if self.root.value == None:  # 判断树是否是空的
            self.root = node
            self.nodeList.append(self.root)
        else:
            parentNode = self.nodeList[0]  # 给nodeList里面相对的父节点添加其子节点
            if parentNode.left == None:
                parentNode.left = node
                self.nodeList.append(parentNode.left)
            else:
                parentNode.right = node
                self.nodeList.append(parentNode.right)
                # 此时parentNode 已有两个节点,所以对它的构造完成,移除它
                self.nodeList.pop(0)

    def front_search(self, root):
        if root is None:
            return None
        stack = []
        val_buf = []
        while root or stack:
            while root:
                val_buf.append(root.value)
                stack.append(root.right)
                root = root.left
            root = stack.pop()
        return val_buf
    def mid_search(self, root):
        if not root:
            return None
        stack = []
        val_buf = []
        while root or stack:
            while root:
                stack.append(root)
                root = root.left
            root = stack.pop()
            val_buf.append(root.value)
            root = root.right
        return val_buf
    def back_search(self, root):
        if root is None:
            return None
        stack = []
        val_buf = []
        while root or stack:
            while root:
                val_buf.append(root.value)
                stack.append(root.left)
                root = root.right
            root = stack.pop()
        return val_buf[::-1]

围棋问题

解析:找出包围的围棋并修改,该问题类型属于岛屿包围问题,采用DFS求解,相关解析可参考leetcode200

class Solution:
    def daoyu(self, grid):
        if not grid:
            return 0
        m = len(grid)
        n = len(grid[0])

        self.direction = [(1, 0), (-1, 0), (0, -1), (0, 1)]
        self.Mark = [[False] * n for _ in range(m)]
        self.res = []
        self.buf = []
        for i in range(m):
            for j in range(n):
                if not self.Mark[i][j] and grid[i][j] == 'O':
                    if self.__dfs(i, j, m, n, grid):
                        self.res.append(self.buf)

        return

    def __dfs(self, i, j, m, n, grid):
        if i == 0 or i == m-1 or j == 0 or j == n-1:
            if grid[i][j] == 'X':
                return True
            else:
                self.Mark[i][j] = True
                return False
        elif not self.Mark[i][j] and grid[i][j] == 'X':
            return True
        elif self.Mark[i][j]:
            return True
        self.buf.append((i,j))
        self.Mark[i][j] = True
        up =  self.__dfs(i-1, j, m, n, grid)
        down = self.__dfs(i+1, j, m, n, grid)
        left = self.__dfs(i, j-1, m, n, grid)
        right = self.__dfs(i, j+1, m, n, grid)

        return up & down & left & right

if __name__ == "__main__":
    sl = Solution()
    grid = [['X', 'X', 'X', 'X'],
            ['X', 'O', 'O', 'X'],
            ['X', 'X', 'O', 'X'],
            ['X', 'O', 'X', 'X']]
    print("_________原始矩阵_________")
    for i in grid:
        print(i)
    sl.daoyu(grid)
    print("_________标记_________")
    for i in sl.Mark:
        print(i)
    print("_________修改位置坐标_________")
    for i in sl.res:
        print(i)
    print("_________修改后矩阵_________")
    for i in sl.res[0]:
        grid[i[0]][i[1]] = 'X'
    for i in grid:
        print(i)

 DFS求解最短路径问题

题目描述:在一个M*N的矩阵中,有一只猫‘2’和老鼠‘3’,存在着障碍物‘1’,需要经过‘0’找到老鼠抓住他,请问最短路径为多少

解题思路,建立一个Mark标记矩阵,单次路径走过的点记录,注意上下左右移动可以使用directions元组技巧,进行深度遍历,最终找到所有路径。

    def cat_mouse(self, grid):
        cat = (-1,0)
        mouse = (-1,0)
        self.directions = [(1, 0), (-1, 0), (0, -1), (0, 1)]
        i = 0
        for row in grid:
            if '2' in row:
                cat = (i,row.index('2'))
            if '3' in row:
                mouse = (i,row.index('3'))
            i += 1
        if cat[0] == -1 or mouse[0] == -1:
            return 0
        m = len(grid)
        n = len(grid[0])
        Mark = [[False] * n for _ in range(m)]
        self.minpath = float('inf')

        def dfs(i, j, distance,Mark):
            if self.minpath < distance:
                return

            for direction in self.directions:
                i_t = i + direction[0]
                j_t = j + direction[1]
                if i_t < 0 or i_t == m or j_t < 0 or j_t == n:
                    continue
                if (i_t,j_t) == mouse:
                    self.minpath = min(self.minpath, distance+1)
                    return
                if Mark[i_t][j_t] == False and grid[i_t][j_t] == '0':
                    Mark[i_t][j_t] = True
                    dfs(i_t, j_t, distance+1, Mark)
                    Mark[i_t][j_t] = False
            return
        dfs(cat[0], cat[1], 0, Mark)
        return self.minpath



if __name__ == "__main__":
    sl = Solution()
    grid = [['2', '0', '0', '1'],
            ['0', '1', '0', '1'],
            ['1', '0', '0', '0'],
            ['3', '0', '1', '0']]
    print("_________原始矩阵_________")
    for i in grid:
        print(i)
    print(sl.cat_mouse(grid))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值