阿里巴巴2023算法面试真题java-python-rust-js-go解法大全

51 阅读7分钟

阿里巴巴的算法面试题以链表、树、图算法和动态规划为主,以下是20道典型的面试真题:

  1. 两数之和:给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
            return new int[] { map.get(complement), i };
        }
        map.put(nums[i], i);
    }
    throw new IllegalArgumentException("No two sum solution");
}

python

def twoSum(nums, target):
    map = {}
    for i in range(len(nums)):
        complement = target - nums[i]
        if complement in map:
            return [map[complement], i]
        map[nums[i]] = i
    raise ValueError("No two sum solution")

go

func twoSum(nums []int, target int) []int {

    m := make(map[int]int)

    for i, num := range nums {

        complement := target - num

        if _, ok := m[complement]; ok {

            return []int{m[complement], i}

        }

        m[num] = i

    }

    panic("No two sum solution")

}

rust

fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {

    let mut map = std::collections::HashMap::new();

    for i in 0..nums.len() {

        let complement = target - nums[i];

        if map.contains_key(&complement) {

            return vec![map[&complement], i as i32];

        }

        map.insert(nums[i], i as i32);

    }

    panic!("No two sum solution");

}

js

function twoSum(nums, target) {
  let map = new Map();
  for (let i = 0; i < nums.length; i++) {
    let complement = target - nums[i];
    if (map.has(complement)) {
      return [map.get(complement), i];
    }
    map.set(nums[i], i);
  }
  throw new Error("No two sum solution");
}
  1. 三数之和:给定一个含有 n 个整数的数组,判断该数组是否含有三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

Java解法:

java
public List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> res = new ArrayList<>();
    if (nums == null || nums.length < 3) return res;
    Arrays.sort(nums);
    for (int i = 0; i < nums.length - 2; i++) {
        if (i > 0 && nums[i] == nums[i - 1]) continue; // 去重
        int left = i + 1;
        int right = nums.length - 1;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum == 0) {
                res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                left++;
                right--;
                while (left < right && nums[left] == nums[left - 1]) left++; // 去重
                while (left < right && nums[right] == nums[right + 1]) right--;  // 去重
            } else if (sum < 0) left++;
            else right--;
        }
    }
    return res; 
}

Python解法:

python
def threeSum(self, nums: List[int]) -> List[List[int]]:
    res = []
    nums.sort()
    for i in range(len(nums)):
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        left, right = i + 1, len(nums) - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total == 0: 
                res.append([nums[i], nums[left], nums[right]])
                left += 1
                right -= 1
                while left < right and nums[left] == nums[left - 1]:
                    left += 1
                while left < right and nums[right] == nums[right + 1]:
                    right -= 1
            elif total < 0: 
                left += 1  
            else:    
                right -= 1      
    return res

Go解法:

func threeSum(nums []int) [][]int {
    sort.Ints(nums)
    n := len(nums)
    res := make([][]int, 0)
    
    for i := 0; i < n; i++ {
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
            
        left, right := i+1, n-1
        for left < right {
            sum := nums[i] + nums[left] + nums[right]
            if sum == 0 {
                res = append(res, []int{nums[i], nums[left], nums[right]})
                left++
                right--
                for left < right && nums[left] == nums[left-1] {
                    left++
                }
                for left < right && nums[right] == nums[right+1] {
                    right--
                }
            } else if sum < 0 {
                left++
            } else {
                right--
            }
        }
    }
    
    return res
}

Rust解法:

impl Solution {
    pub fn three_sum(nums: Vec<i32>) -> Vec<Vec<i32>> {
        let mut nums = nums;
        nums.sort();
        let mut res = Vec::new();
        
        for i in 0..nums.len()-2 {
            if i > 0 && nums[i] == nums[i-1] {continue;}
            
            let mut left = i + 1;
            let mut right = nums.len() - 1;
            
            while left < right {
                let sum = nums[i] + nums[left] + nums[right];
                
                if sum == 0 {
                    res.push(vec![nums[i], nums[left], nums[right]]);
                    left += 1; right -= 1;
                    
                    while left < right && nums[left] == nums[left-1] {left += 1;}
                    while left < right && nums[right] == nums[right+1] {right -= 1;}
                } else if sum < 0 {
                    left += 1;
                } else {
                    right -= 1;
                }
            }
        }
        
        res
    }
}

JS解法:

var threeSum = function(nums) {
    let res = [];
    nums.sort((a, b) => a - b);
    for (let i = 0; i < nums.length - 2; i++) {
        if (i > 0 && nums[i] === nums[i - 1]) continue;
        let left = i + 1;
    let right = nums.length - 1;
    while (left < right) {
        let sum = nums[i] + nums[left] + nums[right];
        if (sum === 0) {
            res.push([nums[i], nums[left], nums[right]]);
            left++;
            right--;
            while (left < right && nums[left] === nums[left - 1]) left++;
            while (left < right && nums[right] === nums[right + 1]) right--;
        } else if (sum < 0) left++;
        else right--;
    }
}
return res;
};
  1. 数组排序,方便去重和左右夹逼
  2. 遍历每个数numsi,左右查找numsleft和numsright,使得numsi + numsleft + numsright == 0
  3. 如果sum == 0,加入结果res,并去重
  4. 如果sum < 0,left指针右移,如果sum > 0,right指针左移
  5. 返回结果res 时间复杂度:O(N^2),其中N是数组长度。我们首先需要O(NlogN)的时间来排序数组,然后需要O(N^2)的时间来枚举和搜索所有可能的三元组。 空间复杂度:O(1)。
  6. 两两交换链表中的节点:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

Java解法:

public ListNode swapPairs(ListNode head) {
    ListNode dummy = new ListNode(0);
    dummy.next = head;
    ListNode cur = dummy;
    


while (cur.next != null && cur.next.next != null) {



    ListNode first = cur.next;



    ListNode second = cur.next.next;



    cur.next = second;



    first.next = second.next;



    second.next = first;



    cur = first;



}



return dummy.next;


}

Python解法:

def swapPairs(self, head: ListNode) -> ListNode:


dummy = ListNode(0)



dummy.next = head



cur = dummy



while cur.next and cur.next.next:



    first = cur.next



    second = cur.next.next



    cur.next = second



    first.next = second.next



    second.next = first



    cur = first 



return dummy.next

Go解法:

func swapPairs(head _ListNode)_ ListNode {


dummy := &ListNode{Next: head}



cur := dummy



for cur.Next != nil && cur.Next.Next != nil {



    first := cur.Next



    second := cur.Next.Next



    cur.Next = second



    first.Next = second.Next



    second.Next = first



    cur = first



}



return dummy.Next


}

Rust解法:

impl Solution {


pub fn swap_pairs(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {



    let mut dummy = Some(Box::new(ListNode::new(0)));



    dummy.as_mut().unwrap().next = head;



    let mut cur = dummy.as_mut().unwrap();



    while let Some(first) = cur.next.as_mut() {



        if let Some(second) = first.next.as_mut() {



            cur.next = Some(second.clone());



            first.next = second.next.clone();



            second.next = Some(first.clone());



            cur = first;



        } else {



            break;



        }



    }



    dummy.unwrap().next



}


}

JS解法:

var swapPairs = function(head) {


let dummy = new ListNode(0);



dummy.next = head;



let cur = dummy;



while (cur.next && cur.next.next) {



    let first = cur.next;



    let second = cur.next.next;



    cur.next = second;



    first.next = second.next;



    second.next = first;



    cur = first; 



}



return dummy.next;


};

算法思路相同,都是使用dummy节点和cur指针,两两交换链表节点,并返回dummy.next作为结果。

时间复杂度:O(n),需要遍历链表一次。

空间复杂度:O(1)。

  1. 反转链表:给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。
  2. 合并两个有序链表:将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
  3. 回文链表:判断给定的链表是否为回文链表。回文链表是指正序和反序读都是一样的链表。
  4. 二叉树的最大深度:给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
  5. 翻转二叉树:给定一个二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧看过来的二叉树的节点值。
  6. 二叉树展开为链表:给定一棵二叉树,将其展开为链表。展开后的链表应该与前序遍历得到的顺序相同。
  7. 从上往下打印出每层二叉树的节点值:给定一个二叉树,按层序从上往下遍历,每层打印一个节点值。
  8. 黑白图像翻转:给定一个黑白图像,实现翻转图像的函数,翻转后,左边的黑色像素点保持不变,而右边的黑色像素点被翻转成白色,右边的白色像素点被翻转成黑色。
  9. 排序链表:给定单链表的头指针和一个数k,实现对链表进行k交换的算法。
  10. 最长回文子串:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
  11. 编辑距离:给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。你可以对一个单词进行如下三种操作:

插入一个字符

删除一个字符

替换一个字符

  1. 跳跃游戏:给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。

  2. 子集:给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集。

  3. 电话号码的字母组合:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。

  4. N皇后:n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数 n ,返回所有不同的 n 皇后问题的解决方案。

  5. 整数拆分:给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

21.最大子序和:给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

  1. 爬楼梯:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
  2. 三角形最小路径和:给定一个三角形 triangle ,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
  3. buying和selling股票的最佳时机:给定一个数组 prices ,它的第 i 个元素 pricesi 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
  4. 组合总数:给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
  5. 合并区间:以数组 intervals 表示若干个区间的集合,其中单个区间为 intervalsi = starti, endi 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
  6. 缺失的第一个正数:给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
  7. 环形链表:给定一个链表,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
  8. 两数相加:给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。
  9. 不同路径:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?
  10. 验证二叉搜索树:给定一个二叉树,判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。

节点的右子树只包含大于当前节点的数。

所有左子树和右子树自身必须也是二叉搜索树。