0 数组
数组(array)是一种线性表数据结构,它用一组连续的内存空间来存储一组具有相同类型的数据。基本特点:支持随机访问,索引与寻址。java 中 int[] arr=new int[100]; 时间复杂度:
Access:O(1)Insert和Delete:平均O(n)。
1 动态数组
所谓动态数组就是其空间会随着数据量变化而变化(扩容或者缩容),比如java ArrayList C++ vector等都是动态数组。如果要实现一个动态数组需要怎么做?1)支持随机访问+寻址 2)空间怎么分配,怎么动态扩容,怎么缩容等
扩容做法:如果空间 不够,重新申请2倍空间大小,将数据拷贝新空间,然后释放旧空间
缩容做法:若空间利用率不到25%,则释放空间
在空数组中连续插入n个元素,来分析一下拷贝次数,数组最终长度2n,其由空间n->2n,拷贝n次数据,n是由n/2扩容到n,需要拷贝n/2 次,依次类推可以得出:总拷贝次数=n+n/2+n/4+…<2n的,可以看插入n个元素的均摊复杂度为o(1)
一次扩容到下一次释放,至少需要再删除0.5n次,比如刚扩容2n,那么至少删除0.5n数据才回触发缩容2n->n。思考一下扩容是2倍扩容,缩容为啥不是1/2空间阈值缩容。如果这么做话可能在临界情况插入和删除交替的话,就会触发扩容、缩容。
2 LeetCode 实战
合并有序数组 https://leetcode-cn.com/problems/merge-sorted-array/submissions/
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
主题思路:开辟一个新的数组,两个索引指针i,j 依次扫描数组数据,谁小先放谁
细节:需要考虑边界问题
具体代码如下。
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
if(nums2==null || n<=0) {
return;
}
int [] tmp=new int[m+n];
int i=0,j=0,k=0;
while(k<m+n){
//处理边界问题
if(j>=n||(i<m && nums1[i]<nums2[j])) {
tmp[k++]=nums1[i++];
}else{
tmp[k++]=nums2[j++];
}
}
k=0;
while(k<m+n){
nums1[k]=tmp[k];
k++;
}
}
}
上面解法整体思路是没有问题,但是从空间复杂度耗费了o(m+n),有没有可以优化空间呢?由于最终数据是合并num1中,考虑能不能倒着放,先处理大的。
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
if(nums2==null || n<=0) {
return;
}
int i=m-1,j=n-1,k=m+n-1;
while(k>=0) {
if(j<0||(i>=0 && nums1[i]>nums2[j])) {
nums1[k--]=nums1[i--];
}else{
nums1[k--]=nums2[j--];
}
}
}
}
有序数组去重 https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
主题思路:从头到尾扫描整个数组,如果这个元素和前一元素是否一样,不一样才保留
细节:边界和数组覆盖
```java
class Solution {
public int removeDuplicates(int[] nums) {
int i=0,k=0;
for(i=0;i<nums.length;i++){
//边界
if(i==0||nums[i]!=nums[i-1]) {
nums[k++]=nums[i];
}
}
return k;
}
}
移动零 https://leetcode-cn.com/problems/move-zeroes/
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
主题思路:从头到尾扫描数组,保留要的元素,放到数组前面,然后把尾部数据处理成0
细节:边界
class Solution {
public void moveZeroes(int[] nums) {
int i=0,k=0;
for(;i<nums.length;i++){
if(nums[i]!=0){
nums[k++]=nums[i];
}
}
while(k<nums.length){
nums[k++]=0;
}
}
}
或者使用双指针法
class Solution {
public void moveZeroes(int[] nums) {
int i=0,j=0,len=nums.length;
while(j<len){
if(nums[j]!=0) {
if(i!=j){
nums[i]=nums[j];
nums[j]=0;
}
i++;
}
j++;
}
}
}