文章目录
1.线性表基础
1.1 线性表
线性表:具有相同特征数据元素的一个有限序列,其中所含元素的个数称为线性表的长度,从不同的角度看,线性表可以有不同的分类,例如:
- 从语言实现的角度:C++和Java
一体式结构:存储表信息的单元和元素存储区以连续的方式安排在一块存储区里,两部分数据的整体形成一个完整的顺序表对象。这种结构整体性强,易于管理。但由于数据元素存储区是表对象的一部分,顺序表创建后,元素存储区就固定了。
分离式结构:表的对象里只保存与表有关的信息(容量和元素个数),实际数据元素存放在另一个独立的元素存储区里,通过链接与基本表对象关联。Java和Python。
- 从存储的角度
顺序型:便于查找
链表型:便于修改
- 访问限制角度
栈和队列又被称为访问受限的线性表,插入和删除遭到了限制,只能在固定的位置进行,而Hash比较特殊,其内部真正存储数据一般是数组,但是访问是通过映射来实现的,因此大部分材料里并不将Hash归结到线性表中。
- 从扩容的角度:
采用分离式结构的顺序表,若将数据区更为存储空间更大的区域,则可以再不改变表对象的前提下对其数据存储去进行了扩充,所有是使用这个表的地方都不必修改。只要程序运行环境(计算机系统)还有空闲存储,这种表结构就不会因为满了而导致操作无法进行。人们把采用这种技术实现的顺序表称为动态顺序表,因为其容量可以在使用中动态变化。
扩容的两种策略:
- 每次扩充增加固定数目存储位置,比如十个,这种策略可称为线性增长。特点:节省空间,但是扩充操作频繁,操作次数多。
- 每次扩充容量加倍。但是会浪费时间
Java基本是扩容时加倍的方式。而Python官方实现中,list实现采用了如下的策略:在建立空表或者很小的表时,系统分配一块能容纳8个元素的存储区;在执行插入操作(insert or append)时,如果元素存储区满,就换一块4倍大的存储区。但如果此时表已经很大(50000),就改变策略,采用加一倍的方法。
1.2 数组
数组是线性表最基本的结构,特点是元素是一个紧密在一起的序列,相互之间不需要记录彼此的关系就能访问,例如月份、星座等。
数组用索引的数字标识每一项数组在数组中的位置,且在大多数编程语言中,索引是从0开始算起的。我们可以根据数组中的索引快速的访问元素。
数组需要注意的点:
- 从0开始记录,length-1结束
- 元素在内存中连续存储,且每个元素占用相同大小的内存
- length的长度一般不等于size(元素个数)
1.3 数组存储元素特征
1.创建一个大小为10的数组,请问此时数组里面是什么?
不同语言处理不同,C语言中每个位置都是一个随机数。在Java中,默认初始化为0。python中可以指定,例如a=[1.2.3.4],就是数组里面有四个元素,而a=[0 for i in range(10)] 这样定义的数组就是10个0.
2.是否可以只初始化一部分位置?初始化的本质是什么?
可以。
初始化的本质就是覆盖已有的值,用你需要的值覆盖原来的0,因为数组本来是一些默认值,然后后替换了一些数。此时如果想知道有效元素的个数,必须在使用一个额外的变量,例如size来标记。
3.上面已经初始化的元素之间是否可以空着,就是间隔着填有效值?
绝对不可以!
要初始化,就必须从前向后连续空间初始化,不可以出现空缺的情况,这是违背数组原则的。你可以进行某种运算期间给部分位置赋值,一旦稳定了,就不可以再出现空位置情况。
4.如果需要的数据是中间某一段怎么办?
用两个变量来表示 [left,right] 该区间是有效的。
5.删除操作?
删除一个元素后,后续元素向前移动。
2.数组基本操作
2.1数组创建和初始化
int [] arr = new int [10];
for(int i=0;i<arr.length;i++)
arr[i]=i;
int []nums = {2,4,5,6,8};//背诵该初始化方式
2.2 查找一个元素
public static int findByELement (int [] arr,int size,int key)
{
for( int i=0;i<size;i++)
{
if(arr[i] == key)
return i;
}
return -1;
}
很多题目本质就是查找问题,数组是查找的最佳载体。很多复杂的算法都是为了提高查找效率的,例如二分查找、二叉树、红黑树、B+树、Hash和堆等等。另一方面很多算法问题本质上都是查找问题,例如滑动窗口问题、回溯问题、动态规划问题等等都是在寻找那个目标结果。
2.3 增加一个元素
能准确处理游标和边界等情况是数组算法题最基础重要的问题之一。所以务必自己亲手写一个。
代码:
public static int addByElementSequence (int[] arr ,int size,int element)
{
if(size >= arr.length)
return -1;
int index = size;
for(int i=0;i<size;i++)
{
if(element<arr[i])
index=i;
break;
}
for(int j=size;j>index;j--)
{
arr[j]=arr[j-1];
}
arr[index] = element;
return index;
}
代码:
public static int addByElementSequence2(int[] arr, int size, int element) {
//判断是否能插入,如果size>length 则空间已满
if (size >= arr.length)
return -1;
int index = 0;
for (int j = size; j > 0; j--) {
if (element >= arr[j - 1]) {
arr[j] = element;
index = j;
return index;
}
arr[j] = arr[j - 1];
}
arr[index] = element;
return index;
}
2.4 删除一个元素
对于删除,不能一边从后移动一边查找,元素可能不存在。
先检查元素是否存在,然后再删除。
public int removeByElement(int[]arr,int size ,int key)
{
int index=-1;
for(int i=0 ;i < size ;i++)
{
if(arr[i] == key)
{
index=i;
break;
}
}
if(index!=-1)
{
for(int i = index+1;i<size;i++)
arr[i-1] = arr[i];
size-=1;
}
return size;
}
单调数组问题
单调性
问题关键在于,同时返回一个能考虑到递增和递减序列的结果。
class Solution {
public boolean isMonotonic(int[] nums) {
return isSorted(nums,true) || isSorted(nums,false);
}
public boolean isSorted (int [] nums,boolean increasing)
{
for(int i=0;i<nums.length;i++)
{
if(increasing){
if(nums[i+1]<nums[i])
return false;
}else{
if(nums[i+1]>nums[i])
return true;
}
}
return true;
}
}
当然,如果同时出现大于和小于就一定不是单调了
class Solution {
public boolean isMonotonic(int[] nums) {
int index1 = -1;
int index2 = -1;
for(int i=0;i<nums.length-1;i++)
{
if(nums[i]<nums[i+1])
index1 = i;
else if(nums[i]>nums[i+1])
index2=i;
}
if(index1==-1||index2==-1)
return true;
else
return false;
}
}
单调性应用
class Solution {
public int searchInsert(int[] nums, int target) {
for(int i =0 ;i<nums.length;i++)
{
if(nums[i]>=target)
return i;
}
return nums.length;
}
}
public int searchInsert(int[]nums,int target){
int n = nums.length;
int left = 0,right=n-1,ans=n;
while(left<right){
int mid = ((right-left)>>1)+left;
if(target<=nums[mid]{
ans = mid;
right=mid-1;
}else{
left = mid+1;
}
}
return ans;
}