文章目录
Sequence List 顺序表
也就是线性表的顺序存储,即用一组地址连续的存储单元一次存储线性表中的数据元素,从而使得逻辑上相邻的元素在物理位置上也是相邻的,有两种空间分配方式:
- 静态分配(静态数组)
- 动态分配(用户分配空间,可以在运行时决定空间分配的大小,这同样也是顺序存储结构)
根据顺序表中元素的特点可以分为:
- 无序顺序表:顺序表中的元素是没有顺序的
- 有序顺序表:顺序表中的元素是有序的(升序或者是降序)
通常一个线性表是这样的:
class List{
private:
ElementType data[MAXSIZE];
int length;
public:
bool insert(int pos, ElementType e);
bool delet(int pos);
bool locate(ElementType e);
}
Basic Operation 基本操作
Insert 插入
将元素插入到0
到 length - 1
(也可以是1
到 length
,说明清楚,保持一致即可)的位置 i
,我们需要先将包括插入位置的元素在内的后面的所有元素后移一位,平均移动次数为
n
/
2
n/2
n/2(考虑最坏的情况和最好的情况,相加除以二即可得到),函数实现:
bool insert(int pos, ElementType e){
if(pos > this->length || pos < 0){
return false;
}
if(this->length >= MAXSIZE){
return false;
}
for(int j = this->length - 1;j >= pos;j--){
this->data[j + 1] = this->data[j]
}
this->data[pos] = e;
this->length++;
return true;
}
Delete 删除
删除所给位置 i
的元素,删除之后,我们需要将删除元素后面的 length - 1 - i
个元素全部前移一位,平均移动次数为
(
n
−
1
)
/
2
(n-1)/2
(n−1)/2。(同理)
bool delet(int pos){
if(pos > this->length - 1 || pos < 0){
return false;
}
if(this->length == 0){
return false;
}
for(int j = pos;j < this->length;j++){
this->data[j] = this->data[j + 1];
}
this->length--;
return true;
}
Locate 查找
查找某个特定值的元素在顺序表中的位置,返回其位置索引(index),平均访问次数为 ( n + 1 ) / 2 (n+1)/2 (n+1)/2。(同理)
int locate(ElementType e){
if(this->length == 0){
return -1;
}
for(int i = 0;i < this->length;i++){
if(this->data[i] == e){
return i;
}
}
return -1;
}
一些算法问题
Reverse 逆置
交换第i
个元素和length - 1 - i
个元素:
void reverse(){
for(int i = 0;i < this->length / 2;i++{
int temp = this->data[i];
this->data[i] = this->data[length - 1 - i];
this->data[length - 1 - i] = temp;
}
return;
}
Merge 合并有序顺序表
合并两个有序的顺序表,只需要遍历到其中一个顺序表末尾即可,复杂度为 O ( N ) = O ( m a x { m , n } ) O(N)=O(max\{m,n\}) O(N)=O(max{m,n}),假定序列都是升序序列:
List merge(List l1, List l2){
List l3;
int i = 0, j = 0, k = 0;
while(i < l1.length && j < l2.length){
if(l1.data[i] > l2.data[j]){
l3.data[k] = l2.data[j];
j++;
}
else{
l3.data[k] = l1.data[i];
i++;
}
k++;
}
// either
while(i < l1.length){
l3.data[k++] = l1.data[i++];
}
// or
while(j < l2.length){
l3.data[k++] = l1.data[j++];
}
l3.length = k;
return l3;
}
Delete by value 按值删除
删除给定值的所有元素
如果是一个有序顺序表,那么给定值的所有元素是在相邻的位置上的,我们只需要统计该位置的元素的个数 k
,然后后面的元素前移 k
位即可:
void deleteByValue(ElementType value){
int start = 0, end = 0;
for(int i = 0;i < this->length;i++){
if(this->data[i] == value]){
start = i;
break;
}
}
for(int i = start;i < this->length;i++){
if(this->data[i] != value){
end = i - 1;
}
}
int k = end - start;
if(k <= 0){
return;
}
for(int i = end + 1;i < this->length;i++){
this->data[i - k] = this->data[i];
}
return;
}
如果不是一个有序顺序表,可以有两种方法:
- 找出每一个值不为
value
的元素,保留 - 记录每一个值为
value
的元素的个数k
,其他元素前移k
位
void deleteByValue1(ElementType value){
// count element whose value not equal to given value
int k = 0;
for(int i = 0;i < this->length;i++){
if(this->data[i] != value){
this->data[k] = this->data[i];
k++;
}
}
}
void deleteByValue2(ElementType value){
// count element with given value
int k = 0;
for(int i = 0;i < this->length;i++){
if(this->data[i] == value){
k++;
}
else{
this->data[i - k] = this->data[i];
}
}
return;
}
删除给定值范围内的所有元素
同理,如果是一个顺序表那么给定范围的元素是连续的,我们依然只需要统计给定范围内元素的个数,然后将后续的元素前移相应的步长即可,只需要修改一下判断条件:
void deleteByValue(ElementType floor, ElementType ceiling){
int start = 0, end = 0;
for(int i = 0;i < this->length;i++){
if(this->data[i] >= floor]){
start = i;
break;
}
}
for(int i = start;i < this->length;i++){
if(this->data[i] >= ceiling){
end = i - 1;
}
}
int k = end - start;
if(k <= 0){
return;
}
for(int i = end + 1;i < this->length;i++){
this->data[i - k] = this->data[i];
}
return;
}
同样如果是有序顺序表,则同样只需要修改一下判断条件即可:
void deleteByValue1(ElementType floor, ElementType ceiling){
// count element whose value not equal to given value
int k = 0;
for(int i = 0;i < this->length;i++){
if(this->data[i] < floor || this->data[i] > ceiling){
this->data[k] = this->data[i];
k++;
}
}
}
void deleteByValue2(ElementType floor, ElementType ceiling){
// count element with given value
int k = 0;
for(int i = 0;i < this->length;i++){
if(this->data[i] >= floor && this->data[i] <= ceiling){
k++;
}
else{
this->data[i - k] = this->data[i];
}
}
return;
}
duplicate removal 去重
如果是一个有序顺序表的话,那么重复的元素将是连续的,那么我们只需要判断下一个元素是否跟前一个元素的值相同,是的话,就增加计数,不是的话就前移相应的步长即可:
void removeDuplicate(){
if(this->length <= 1){
return;
}
int k = 0;
for(int i = 1;i < this->length;i++){
if(this->data[i] == this->data[i - 1]){
k++;
}
else{
this->data[i - k] = this->data[i];
}
}
return;
}
如果是一个无序的顺序表,要在时间上尽可能高效的话,我们可以考虑用空间换取时间的方法,也就是用一个散列表记录每一个元素对应的值是否出现过,则增加计数,否则前移相应的步长,可以去了解散列表的用法,实现不难。