java常见算法题(1)

一些在leetCode中高频面试热题中,针对各题型的简单基础题笔记。

冒泡排序

冒泡排序的基本思想:相邻的数据进行两两比较较小的数放在前面,较大的数放在后面,这样一趟下来,最大的数就被排在最后,第二趟也是如此,如此类推,直到所有的数据排序完成。
在这里插入图片描述
上图演示的是快速排序的流程,可以看到每一轮排序都能将当前排序序列中最大的元素放到最后。

核心算法代码:

//冒泡排序
public static void bubbleSort(int[] arr) {
    int n = arr.length;
    //外部循环控制排序的趟数。冒泡排序的每一趟会将最大的元素"冒泡"到数组的末尾,因此需要执行 n-1 趟,其中 n 是元素的总个数
    for (int i = 0; i < n - 1; i++) {
        //内循环控制每趟比较的次数。由于每一趟都会将一个最大的元素沉到数组末尾,所以内循环次数逐渐减小。
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // 交换arr[j]和arr[j+1]
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

快速排序

快速排序的基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。具体流程如下:
从数组中挑出一个元素,称为 “基准”
重新排序数列,所有元素
比基准值小的摆放在基准左边
,所有元素比基准值大的摆在基准的右边(从大到小排序则倒过来)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
在这里插入图片描述
上图演示的是以6为基准进行的一轮快速排序。
(图片来源:https://blog.csdn.net/MaYingBo928/article/details/130281848)

核心算法代码:

 // 快速排序
    public static void quickSort(int[] arr, int left, int right) { //这里的三个输入分别是待排序数组、开始排序的数组下标、结束排序的数组下标
        if(left > right) {//当开始的下标大于结束的下标,就失去意义了直接return
            return;
        }
        int temp = arr[left];// 基准点
        int i = left;
        int j = right;
        while(i < j) {//从两个方向遍历数组
            while(temp <= arr[j] && i<j) {//右测的遍历数据比基准大时则不改变位置
                j--;
            }
            while(temp >= arr[i] && i<j) {//左测的遍历数据比基准小时则不改变位置
                i++;
            }
            if(i<j) {//否则左侧遍历数据和右侧遍历数据互换位置
                int m = arr[j];
                arr[j] = arr[i];
                arr[i] = m;
            }
        }
        //以下两步是为了调整基准点的位置至数组中间
        arr[left] = arr[j];
        arr[j] = temp;
        //对基准点两侧再进行快速排序
        quickSort(arr,left,j-1);
        quickSort(arr,j+1,right);
    }

数组相关算法题

移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,需要在不复制数组的情况下原地对数组进行操作。

示例 :
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

解题思路: 此题可以用到类似于上文所提及的快速排序的思想,将0作为划分点,让不为0的数放在数组的左边,等于0的数全部放在数组的右边。具体流程为:设置a、b两个指针,a为当前还未处理的数组序列中的第一个数组下标,b为当前状态下数组序列中第一个值为0的数组下标。文字可能有点难理解,流程图如下:
在这里插入图片描述
核心算法代码:

	public void moveZeroes(int[] nums) {
		if(nums==null) { //如果输入等于空则直接返回
			return;
		}
		//两个指针i和j
		int i = 0;
		int j = 0;
		for(i=0;i<nums.length;i++) {
			if(nums[i]!=0) {//当前元素!=0,就把其交换到左边,等于0的交换到右边
				int temp = nums[i];
				nums[i] = nums[j];
				nums[j] = temp;
				j++;
			}
		}
	}

哈希相关算法题

两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现,可以按任意顺序返回答案。

示例:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,所以返回 [0, 1] 。

解题思路: 用哈希表来实现,因为hashMap的数据结构是kv键值对,可以把数组值作为key,当前值对应的下标作为value存进去(由于返回的是下标,所以下标为value)

核心算法代码:

//哈希表实现两数之和
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<nums.length;i++)
        {
            if(map.containsKey(target-nums[i])) //当哈希表中包含了与当前数组值相加等于目标值的key,就返回对应的两个数组下标
            {
                return new int[]{map.get(target-nums[i]),i};
            }
            map.put(nums[i],i); //把当前数组值和对应下标放进哈希表
        }
        return null;
    }


二分查找

搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。

示例1:
输入: nums = [1,3,5,6], target = 5
输出: 2(2为数组中值为5的下标,下标是从0开始计算的)

示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1(数组中没有值为2的成员,所以插入在1和3之间,所以此时返回插入位置的下标为1)

解题思路: 利用二分查找来实现,二分查找的具体思想是:从数组中间(记为mid)开始搜索,如果此时正好等于目标值,则结束循环,返回值;否则比较需要查找的数和mid的大小关系,如果比mid大,则在mid右侧的子数组中再进行二分查找,如果比mid小,则在mid右侧进行二分查找

核心算法代码:

class Solution {
    public int searchInsert(int[] nums, int target) {
            int left =  0;
            int right = nums.length - 1;//定义区间值
            while(left <= right) {
                int mid = (left + right)/2;//定义中间值的运算规则
                if(nums[mid] == target) {//如果mid的值等于目标值
                    return mid;
                }else if (nums[mid] < target) {//如果mid的值小于目标值
                    left = mid + 1;//在mid右侧继续二分查找
                }else {//否则
                    right = mid -1;//在mid左侧继续二分查找
                }
            }
            return left;//最后返回下标值
    }
}

链表相关算法题

Java中链表的结构如下:


 public class ListNode {
      int val;
      ListNode next;
      ListNode() {}
      ListNode(int val) { this.val = val; }
      ListNode(int val, ListNode next) { this.val = val; this.next = next; }
  }
 

其中next是当前链表元素指向下一链表元素的指针,val是当前链表的元素值。

反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例:
在这里插入图片描述
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

解题思路:
将链表的next指针倒置,然后向后依次遍历链表。流程如下图所示:
在这里插入图片描述

核心算法代码:

public ListNode reverseList(ListNode head){
        ListNode temp = null;
        ListNode cur = head; //当前处理结点指针,初始为头结点
        ListNode pre = null; //cur结点前一个结点的指针
        while(cur!=null){
            //将链表的next指针倒置,并向后遍历链表
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }

判断链表是否有环

给你一个链表的头节点 head ,判断链表中是否有环。(如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。)
如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例:

在这里插入图片描述
输入:head = [3,2,0,-4]
输出:true

解题思路: 可以利用Set集合元素不可重复的特点来解决问题。可以把已经访问过的结点存进HashSet中,每次到达一个结点就进行判断,如果HashSet中已经存在该节点,则说明该链表存在环。

核心算法代码:

public boolean hasCycle(ListNode head) {
    Set<ListNode> seen = new HashSet<ListNode>(); //设置一个HashSet来存放已经访问过的链表结点
    while (head != null) {
        if (seen.add(head)==false) { //HashSet的add方法是用来向集合中添加元素的,它会有boolean类型的返回值,true则是添加成功,false则是添加失败,这里添加失败即为HashSet中已存在该结点
            return true; //说明链表存在环
        }
        head = head.next;
    }
    return false;
}

栈结构相关算法题

栈是一种特殊的线性表,它只允许在固定的一端进行插入和删除元素操作,即满足先进后出的条件。
在这里插入图片描述

栈相关的算法题一般只需了解java中的栈类(Stack类)中的常见方法使用即可。
empty( ):判断堆栈是否为空。
peek( ):返回栈顶端的元素,但不从堆栈中移除它。
pop( ):移除堆栈顶部的对象,并将该对象作为方法的返回对象。
push (Object element):把元素压入栈中。
search(Object element):返回对象在堆栈中的位置,它是以1为基数。

有效的括号

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

示例:
输入:s = “()[]{}”
输出:true

解题思路: 设计一个辅助栈,将输入的字符串拆分成字符数组,逐个字符进行匹配判断:匹配到某类左括号时,将该类左括号的对应右括号字符push压入栈中;当匹配到右括号时,通过pop来判断此时栈顶元素是否与当前右括号相等,若相等则继续匹配,不相等则直接返回false。若到最后全部匹配完时,栈内元素全部出栈,则说明匹配成功,返回true。

由于栈先进后出的特性,左括号和右括号的相应匹配总能匹配到相距最近的。
例如:{ ( [ ] ) } 。对于这个字符串,当 { ( [ 被压进栈后,字符 “[” 对应的右括号 “]” 会在栈的栈顶,此时下一个右括号字符 “]” 即能与之匹配,以此类推。

核心算法代码:

    public boolean isValid(String s) {
        if(s.isEmpty()) //字符串为空则直接返回
            return true;
        Stack<Character> stack=new Stack<Character>(); //用辅助栈来解决该问题
        for(char c:s.toCharArray()){ //将字符串转换为字符数组
            if(c=='(')
                stack.push(')');
            else if(c=='{')
                stack.push('}');
            else if(c=='[')
                stack.push(']');
            else if(stack.empty()||c!=stack.pop())
                return false;
        }
        if(stack.empty())
            return true;
        return false;
        }
  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值