首页 > 编程语言 >算法刷题:数组题(持续更)

算法刷题:数组题(持续更)

时间:2023-08-13 12:33:07浏览次数:51  
标签:right return nums int ++ 算法 数组 刷题 left

算法刷题系列:


力扣链接:
删除有序数组中的重复项
删除排序链表中的重复元素
移除元素
移除链表元素
两数之和
反转字符串
反转链表
验证回文串
验证回文串 II


目录


快速排序

原理

(自己懒得做图,图片来自别的博客)
下面序列:
快排00

把第一位57作为基准位,用变量把它存起来,因为一会就没了

  • 把所有比57小的数放在57的左面,把比57大的数放在57的右面
  • 两边同时进行,左边找大的,右边找小的,把小的放左边,大的放右边,具体操作如下:

第一趟:从指针j开始,24小于57,放到左边,把57覆盖掉
01

之后:指针i右移,指向68,68>57,放到右边
02

之后:指针j左移指向33,33<57,放到左边
03

之后:指针i右移指向59,59>57,放右边
04

之后:指针j左移指向96,96>57,j再左移指向28,28<57,放左边
05

之后:指针i右移指向52,52<57,i继续右移指向72,72>57,放右边
06

之后:指针j左移,与指针i重合指向NULL,这时放入57
07

这时发现左边的数都比57小,右边都比57大

然后再对57左边的数,即:0到i-1进行快速排序(同样操作,把24作为基准,左边小,右边大),对57右边的数,即:i+1到n进行快速排序(以72位基准,左边小,右边大)直到不能再进行排序为止;

代码实现

优化:扰动

int pvt = nums[mid];
swap(nums, left, mid);
static void quickSort(int[] nums, int left, int right){
if(left > right) return;

int mid = (left + right) >>> 1;
int l = left;
int r = right;
int pvt = nums[mid]; // 基准元素

swap(nums, left, mid);
while(l < r){
while(l < r && nums[r] >= pvt) r--;
if(l < r) nums[l++] = nums[r]; // l 指向空位

while(l < r && nums[l] < pvt) l++;
if(l < r) nums[r--] = nums[l]; // r 指向空位
} nums[l] = pvt; // 基准元素放入 lr 指向的空位

quickSort(nums, left, l - 1);
quickSort(nums, l + 1, right);
}

快慢指针

快慢指针sf:
同向移动:

  • 同步移动:
  1. 速度倍数:
  2. 相对速度:
  3. 相邻先后 (pcn):
  4. 齐头并进:是否需要齐头,之后再并进
  • 异步移动:
  1. 慢指针符合条件再动

异向移动:

  • 左右指针
  • 边向中
  • 中向边

指针指向:

  • 空位(快速排序)
  • 处理点

注意事项

if(str.charAt(l) != str.charAt(r)){

不如

char lval = str.charAt(l);
char rval = str.charAt(r);
if(lval != rval){

的效率高

异步移动

删除有序数组的重复项

代码实现

public int removeDuplicates(int [] nums){
int slow = 0;
for(int fast = 0; fast < nums.length; ++fast){
if(nums[fast] != nums[slow]){
nums[++slow] = nums[fast];
}
}
return slow + 1;
}

对比链表的删除重复项代码

很类似:

public ListNode deleteDuplicates(ListNode head){
if(head == null) return null;

ListNode slow = head;
for(ListNode fast = head; fast != null; fast = fast.next){
if(fast.val != slow.val){
slow.next = fast;
slow = slow.next;
}
}
slow.next = null;
return head;
}

移除元素

代码实现

int removeElement(int [] nums, int val){
int sl = 0;
for(int fa = 0; fa < nums.length; ++fa){
if(nums[fa] != val){
nums[sl] = nums[fa];
sl++;
}
}
return sl;
}

对比移除链表元素

ListNode removeElements(ListNode head, int val){
ListNode dummy = new ListNode();

ListNode sl = dummy;
for(ListNode fa = head; fa != null; fa = fa.next){
if(fa.val != val){
sl.next = fa;
sl = sl.next;
}
}
return dummy.next;
}

异向而行

两数之和

暴力解法(\(O(N^2)\),56ms)

class Solution {
public int[] twoSum(int[] nums, int target) {
int [] res = new int [2];
for(int i = 0; i < nums.length; i++){
for(int j = i + 1; j < nums.length; j++){
int sum = nums[i] + nums[j];
if(sum == target) {
res[0] = i;
res[1] = j;
return res;
}
}
}
return res;
}
}

快排 + 左右指针(\(O(NlogN)\),2ms)

但是排序之后,索引位置会发生变化
所以需要保存排序之前的数组

class Solution {
public int[] twoSum(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;

int [] src = new int [nums.length]; // 保存源数组
for(int i = 0; i < nums.length; i++){
src[i] = nums[i];
}
quickSort(nums, 0, right); // 排序

int [] res = new int [2];

int sum = 0;
while(left < right){
sum = nums[left] + nums[right];
if(sum < target){
left ++;
} else if(sum > target){
right --;
} else {
int i = 0;
int lval = nums[left];
int rval = nums[right];
while(true){
if(src[i] == lval){
res[0] = i;
break;
} i++;
} i = 0;
while(true){
if(src[i] == rval){
if(i == res[0]){
i++;
continue;
}
res[1] = i;
break;
} i++;
}
break;
}
}
return res;
}
}

哈希表空间换时间(\(O(N)\))

单向遍历解法(2ms)

将两个变量中的一个变量,用空间(哈希表)转化成常量,空间换时间

class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for(int l = 0; l < nums.length; l++){
int rval = target - nums[l];
if(map.containsKey(rval)){
return new int []{map.get(rval), l};
}
map.put(nums[l], l);
}
// return new int []{0, 0};
return new int [2];
}
}
优化:边向中左右指针(0ms)

类似快速排序的左右指针从边界向中间迭代:

class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for(int l = 0, r = nums.length - 1; l <= r; l++, r--){
int rval = target - nums[l];
if(map.containsKey(rval)){
return new int []{map.get(rval), l};
}
map.put(nums[l], l);

int lval = target - nums[r];
if(map.containsKey(lval)){
return new int []{map.get(lval), r};
}
map.put(nums[r], r);
}
// return new int []{0, 0};
return new int [2];
}
}

反转字符串(\(O(N)\))

代码实现

void reverseString(char [] s){
int left = 0;
int right = s.length - 1;
while(left < right){
swap(s, left, right);
left++;
right--;
}
}

对比反转链表

ListNode reverse(ListNode head){
if(head == null && head.next == null){
return head;
}
ListNode pre = null; // 处理完成
ListNode cur = head; // cur 是待处理的点
ListNode nxt = null;
while(cur != null){ // 没有需要处理的
nxt = cur.next;

cur.next = pre; // 处理内容:反转

pre = cur; // 跟随
cur = nxt;
}
return pre;
}

验证回文串(\(O(N)\),1ms击败100%)

这个本质也是相向而行,但是在相向而行中多处理了很多步骤

class Solution {
public boolean isPalindrome(String s) {
int left = 0;
int right = s.length() - 1;
while(left < right){
// 大写转换为小写
char lval = castChar(s.charAt(left));
char rval = castChar(s.charAt(right));
// 是否是小写字母 或数字
if(lval > 'z' || lval < 'a' ){
if(lval > '9' || lval < '0'){
left ++;
continue;
}
}
if(rval > 'z' || rval < 'a' ){
if(rval > '9' || rval < '0'){
right --;
continue;
}
}
// 判断是否回文串
if(lval != rval){
return false;
}
// 迭代
left ++;
right --;
}
return true;
}
char castChar(char c){ // 将大写字母转换为小写字母
if(c >= 'A' && c <= 'Z'){
return (char)((int)c - ('A' - 'a'));
}
return c;
}
}

验证回文串 II(\(O(N)\),4ms击败100%)

全是英文小写字母,重点是可以有一次机会修改,这个可以理解为左右指针指向了不一致的值可以删除左指针值或者右指针值,再看看是不是回文串

class Solution {
public boolean validPalindrome(String s) {
int l = 0;
int r = s.length() - 1;
while(l < r){
char lval = s.charAt(l);
char rval = s.charAt(r);
if(lval != rval){
boolean lok = palindrome(s, l + 1, r);
if(lok) break;
boolean rok = palindrome(s, l, r - 1);
if(rok) break;
return false;
}
l++; r--;
}
return true;
}
public boolean palindrome(String s, int l, int r){
while(l < r){
char lval = s.charAt(l);
char rval = s.charAt(r);
if(lval != rval){
return false;
}
l++; r--;
}
return true;
}
}

标签:right,return,nums,int,++,算法,数组,刷题,left
From: https://www.cnblogs.com/luoyicode/p/17626396.html

相关文章

  • C#快速排序算法
    快速排序实现原理快速排序(QuickSort)是一种常用的排序算法,它基于分治的思想,通过将一个无序的序列分割成两个子序列,并递归地对子序列进行排序,最终完成整个序列的排序。其基本思路如下:选择数组中的一个元素作为基准(pivot)。将数组中小于等于基准的元素放在基准的左边,将大于基......
  • 数组
    数组是存放在连续内存空间上的相同类型数据的集合。数组可以方便地通过下标索引的方式获取到下标下对应的数据。因为数组的内存空间地址是连续的,所以在删除和添加元素的时候,就要移动其他元素的地址。数组的元素是不能删除的,只能覆盖。二维数组的存储如下: ......
  • [算法考研笔记]mm算法随笔[成绩划分][回溯0-1][得分][字段和][聪明小偷][股票买卖]
    mm算法随笔学习笔记(回溯算法)回溯<---->递归1.递归的下面就是回溯的过程回溯法是一个纯暴力的搜索、有的时候暴力求解都没有办法,用回溯可以解决。回溯法解决的问题:组合问题如:1234两两组合切割问题如:一个字符串有多少个切割方式,或者切割出来是回文子集问题:1......
  • 前端开发工程师需要掌握哪些算法?
    一个程序员一生中可能会邂逅各种各样的算法,但总有那么几种,是作为一个程序员一定会遇见且大概率需要掌握的算法。作为一名前端开发工程师,今天就通过这个话题和文章来聊聊前端开发工程师需要掌握的算法有哪些呢。......
  • 数组及元组
    第3章数组及元组3.1定长数组定义长度不变的数组可以使用ArrayScala数组的底层实际上是Java数组。例如字符串数组在底层就是Java的String[],整数数组在底层就是Java的Int[]valnums=newArray[Int](10)//生成10个整数的数组,所有元素初始化为0valnums=newArray[String](......
  • 基于GMM高斯混合模型的语音信息身份识别算法的matlab仿真
    1.算法理论概述一、引言     语音信息身份识别是指通过声音信号对个体进行身份识别的过程。目前,语音信息身份识别已经成为语音处理领域的一个热门研究方向。在语音信息身份识别中,高斯混合模型(GMM)是一种被广泛应用的方法。本文将详细介绍基于GMM的语音信息身份识别算法的实......
  • 树状数组
    前置知识:lowbit运算\(lowbit(x)\)表示正整数\(x\)在二进制表示下最低位的\(1\)跟后面的\(0\)构成的数值,有\(lowbit(x)=x\)&$($~\(~x+1)\),即\(lowbit(x)=x\)&\(-x\),理由如下:\(lowbit(x)\)是最后一位\(1\)所以跟前面的位没啥关系,祂在二进制表示下肯定就是\(......
  • 算法——初级排序算法之快速排序
    介绍快速排序可以说是应用最广泛的排序算法了,主要是因为实现简单、适用于各种不同的输入数据且在一般应用中比其他排序算法都要快得多。快速排序是一种基于分治思想的排序算法。它将一个数组分成两个子数组,将两部分独立地排序。原理设置两个变量low、high,排序开始时:low=0,hig......
  • acwing 116.飞行员兄弟 (算法竞赛进阶指南 p48 t1 ) 题解
    原题链接https://www.acwing.com/problem/content/description/118/题目描述“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有16个把手的冰箱。已知每个把手可以处于以下两种状态之一:打开或关闭。只有当所有把手都打开时,冰箱才会打开。把手可以表示为一个4х4的矩阵,您可以......
  • 算法——初级排序算法之归并排序
    介绍将两个有序的数组归并成一个更大的有序数组,这种算法叫归并排序特点优点:能够保证将任意长度为N的数组排序所需时间和NlogN成正比缺点:所需的额外空间和N成正比1、**原地归并**实现归并的一种直截了当的办法是将两个不同的有序数组归并到每三个数组中,两个数组中的元素......