书籍:剑指offer,漫画算法(小灰)
怎么刷:限时,一题多解,评论,记录和注释,不会就在题目和答案之间反复横跳,一定要自己写出来,写完思考一下
2023/3/2
二分查找
二分查找也称折半查找(Binary Search),折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列
首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
关键步骤:设置两个变量来规定头和尾从而来确定中间(二分就是找中间和目标数比较)
而中间数=(right-left)/2+left
class Solution {
public int search(int[] nums, int target) {
int n=nums.length-1;
int left=0;
int right=n;
int mid;
while(right>=left){
mid=(right-left)/2+left;//不用mid=(right+left)/2是因为怕加法会溢出
int num=nums[mid];
if(num==target){
return mid;
}else if(num<target){
left=mid+1;
}else {
right=mid-1;
}
}
return -1;
}
}
例题:
分析:
1.版本数组是有序排列的
2.版本错误和正确是有个界限,就是要找到这个界限
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left=1;
int right=n;
int mid=0;
while (right>=left) {
mid=(right-left)/2+left;
if (isBadVersion(mid)){
right=mid-1;
}else{
left=mid+1;
}
}
return left;
}
}
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int flag = 0;
while (right >= left) {
int mid = (right - left) / 2 + left;
if (nums[mid] == target) {
flag = 1;
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
}
public int[] sortedSquares(int[] nums) {
int[] a=new int[nums.length];//数组的定义
int t=0;
for (int i = 0; i <nums.length ; i++) {
a[i]=nums[i]*nums[i];
}
// Arrays.sort(a);
for (int i = 0; i <a.length ; i++) {
for (int j = i; j <a.length ; j++) {
if (a[i]>a[j]){
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
}
return a;
}
1、直接插入排序
int* sortedSquares(int* a, int n, int* returnSize){
*returnSize=n;
if(n==0) return a;
a[0]=a[0]*a[0];
int j;
for(int i=1;i<n;i++){
int temp=a[i]*a[i];
for(j=i-1;j>=0;j--){
if(temp<a[j]) a[j+1]=a[j];
else break;
}
a[j+1]=temp;
}
return a;
}
2、折半插入排序
int* sortedSquares(int* a, int n, int* returnSize){
*returnSize=n;
if(n==0) return a;
a[0]=a[0]*a[0];
int j;
for(int i=1;i<n;i++){
int temp=a[i]*a[i];
int low=0,high=i-1;
while(low<=high){
int mid=(low+high)/2;
if(temp>a[mid]) low=mid+1;
else high=mi-1;
}
for(j=i-1;j>high;j--){
a[j+1]=a[j];
}
a[j+1]=temp;
}
return a;
}
3、选择排序
int* sortedSquares(int* a, int n, int* returnSize){
*returnSize=n;
if(n==0) return a;
int k=0;
for(int i=0;i<n;i++){
a[k++]=a[i]*a[i];
}
for(int i=0;i<n-1;i++){
for(int j=i+1;j<n;j++){
if(a[j]<a[i]){
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
return a;
}
4、冒泡排序
int* sortedSquares(int* a, int n, int* returnSize){
*returnSize=n;
if(n==0) return a;
int k=0;
for(int i=0;i<n;i++){
a[k++]=a[i]*a[i];
}
for(int i=0;i<n;i++){
for(int j=0;j<n-i-1;j++){
if(a[j+1]<a[j]){
int temp=a[j+1];
a[j+1]=a[j];
a[j]=temp;
}
}
}
return a;
}
5、带判断条件的冒泡排序
int* sortedSquares(int* a, int n, int* returnSize){
*returnSize=n;
if(n= =0) return a;
int k=0;
for(int i=0;i<n;i++){
a[k++]=a[i]*a[i];
}
int flag=1;
while(n>1&&flag==1){
flag=0;
for(int j=0;j<n-1;j++){
if(a[j+1]<a[j]){
flag=1;
int temp=a[j+1];
a[j+1]=a[j];
a[j]=temp;
}
}
}
return a;
}
6、快排
int Pattion(int *a,int low,int high){
int temp=a[low],pivoty=a[low];
while(low<high){
while(a[high]>=pivoty&&low<high) high--;
a[low]=a[high];
while(a[low]<=pivoty&&low<high) low++;
a[high]=a[low];
}
a[low]=temp;
return low;
}
void QuickSort(int *a,int low,int high){
int p;
if(low<high){
p=Pattion(a,low,high);
QuickSort(a,low,p-1);
QuickSort(a,p+1,high);
}
}
int* sortedSquares(int* a, int n, int* returnSize){
*returnSize=n;
if(n==0) return a;
int k=0;
for(int i=0;i<n;i++){
a[k++]=a[i]*a[i];
}
QuickSort(a,0,n-1);
return a;
}
7、归并排序
可能我写的问题 内存超限了
void MergeSort(int *a,int low,int mid,int high){
int *b=(int *)malloc(sizeof(int)*(high+1));
for(int i=low;i<=high;i++){
b[i]=a[i];
}
int i=low,j=mid+1,temp=low;
while(i<=mid&&j<=high){
if(b[i]<b[j]){
a[temp++]=b[i++];
}
else a[temp++]=b[j++];
}
while(i<=mid) a[temp++]=b[i++];
while(j<=high) a[temp++]=b[j++];
}
void Merge(int *a,int low,int high){
int mid;
if(low==high)return ;
else{
mid=(low+high)/2;
Merge(a,low,mid);
Merge(a,mid+1,high);
MergeSort(a,low,mid,high);
}
}
int* sortedSquares(int* a, int n, int* returnSize){
*returnSize=n;
if(n==0) return a;
int k=0;
for(int i=0;i<n;i++){
a[k++]=a[i]*a[i];
}
Merge(a,0,n-1);
return a;
}
8、双指针
int* sortedSquares(int* a, int n, int* returnSize){
*returnSize=n;
if(n==0) return a;
int p=0,q=-1;
for(int i=0;i<n;i++){
if(a[i]>=0) {
p=i;
q=i-1;
break;
}
}
int k=0;
int *res=(int *)malloc(sizeof(int)*n);
while(p<n&&q>=0){
if(a[p]*a[p]<a[q]*a[q]){
res[k++]=a[p]*a[p];
p++;
}
else{
res[k++]=a[q]*a[q];
q--;
}
}
while(p<n) {
res[k++]=a[p]*a[p];
p++;
}
while(q>=0){
res[k++]=a[q]*a[q];
q--;
}
return res;
}
public void rotate(int[] nums, int k) {
int n = nums.length;
int[] newArr = new int[n];
for (int i = 0; i < n; i++) {
newArr[(i + k) % n] = nums[i];
}
System.arraycopy(newArr, 0, nums, 0, n);
}
方法一:
array在这里是一个指针,而不是数组。使用 malloc 函数动态分配的内存返回的是指向分配内存首地址的指针。
在这个例子中,array是一个指向 int 类型的指针,它指向了一个动态分配的内存块,该内存块大小为 numsSize * sizeof(int) 字节,用于存储整数数据。
虽然我们可以通过 array[i] 这样的方式来访问分配的内存块中的元素,但 array本身仍然是一个指针。用完后要free
calloc函数是C语言中的一个动态内存分配函数之一,它可以动态地分配内存并初始化为0
时间空间复杂度o(n)遍历一次数组,需分配内存空间
int removeDuplicates(int* nums, int numsSize) {
if(numsSize<=1){
return numsSize;
}
int k=0;
int *array=(int*)malloc(numsSize*sizeof(int));
array[0]=nums[0];
for(int i=1;i<numsSize;++i){
if(array[k]!=nums[i]){
k++;
array[k]=nums[i];
}
}
for(int i=0;i<=k;++i){
nums[i]=array[i];
}
free(array);
return k+1;
}
方法2:
双指针(不是链表什么的才有指针,i,j这些也是指针)时间复杂度on,空间o1
int removeDuplicates(int* nums, int numsSize) {
if(numsSize<=1) return numsSize;
int i=0;
int j=1;
for(j;j<numsSize;++j){
if(nums[i]!=nums[j]){
i++;
nums[i]=nums[j];
}
}
return i+1;
}
第一种 暴力解法
遍历数组,第一个元素与target的差是否在后面数组,没有就遍历下一个,两层循环 时间复杂度 on2
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] a=new int[2];
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
if(nums[j]==target-nums[i]){
a[0]=i;
a[1]=j;
}
}
}
return a;
}
}
第二种 哈希表(key value)
只需遍历一次,减少时间复杂度,先把第一个元素放进哈希表,然后遍历数组,如果和target之差没有在哈希表里,就把数组存进哈希表里,时间复杂度on
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> hashtable=new HashMap<>(nums.length-1);
hashtable.put(nums[0],0);
for(int i=1;i<nums.length;++i){
if(hashtable.containsKey(target-nums[i])){
return new int[]{hashtable.get(target-nums[i]),i};
}
hashtable.put(nums[i],i);
}
return new int[0];
}
}
方法1:双指针,右指针遍历数组,如果值等于val的话,右指针右移一位,左指针不动,记录这个位置以后是要被代替的
如果右指针不等于val,把右指针的值赋给左指针位置的数组格,左右指针加一
int removeElement(int* nums, int numsSize, int val) {
int k=0;
int left=0;
for(int right=0;right<numsSize;++right){
if(nums[right]!=val){
nums[left]=nums[right];
k++;
left++;
}
}
return k;
}
总结一下:这种原地删数组的,通常用双指针,右指针遍历数组,负责判断元素是否符合要求,符合要删的右指针就右移,不符合的话把右指针的值赋值给左指针,左指针就是负责记录需要被删的位置,等右指针找到不需要删的元素,就把右指针的值赋给左指针,左右指针右移