一、数组
1.概念
特点:
内存是连续的
优点:
下标访问(随机访问)时间复杂度是O(1)
末尾位置增加删除元素时间复杂度是O(1)
访问元素前后相邻位置的元素非常方便
缺点:
非末尾位置增加删除元素需要进行大量的数据移动O(n)
搜索的时间复杂度 无序数组-线性搜索O(n)
有序数组-二分搜索O(logn)
数组扩容消耗比较大
2.动态数组实现
2.1、创建操作对象
template<class T>
class DARR
{
public:
DARR(int sizeArr = 10); //构造函数
void PushBack(T value); //末尾插入
void PopBack();//末尾删除
void Insert(int pos, T value); //按位置增加元素
void erase(int pos); //按位置删除
T find(T value); //元素查询
void print(); //打印
~DARR(); //析构
private:
void expand(int size); //扩容
T* pAddr; //存放数据的地址
int size; //当前有多少个元素
int capacity; //容纳的最大数
};
2.2、构造函数
DARR(int sizeArr = 10) :size(0), capacity(sizeArr) {
this->pAddr = new T[sizeArr];
}
数组默认容量为10(后期不够用可以扩容),初始化列表初始数组的容纳数以及最大容纳数
2.3、数组扩容
void expand(int size) {
T* p = new T[size];
memcpy(p, this->pAddr, sizeof(T) * this->capacity);
delete[] this->pAddr;
this->pAddr = p;
this->capacity = size;
}
创建一个可容纳size个数组元素的内存空间,将原来的数组通过memcpy函数拷贝到新创建的内存中,并且把老内存delete掉,将维护数组指针的pAddr指向新内存首地址,并且更新数组最大的容纳数
2.4、末尾插入元素
void PushBack(T value) { //末尾插入
if (this->size == this->capacity) {
this->expand(this->capacity+10);//扩容
}
this->pAddr[this->size] = value;
this->size++;
}
首先要判断当前数组容纳的元素数量是否等于数组最大容纳数,如果条件满足,则将该数组扩容10个容量,之后将新增的这个元素放入到数组中,容量数加一
2.5、末尾删除
void PopBack() { //末尾删除
if (this->size == 0) {
return;
}
this->size--;
}
先判断该数组是不是空,若是则返回,否则数组容量数减一
2.6、按位置增加元素
void Insert(int pos, T value) { //按位置增加元素
if (pos<0 || pos>this->size) {
return;
}
if (this->size == this->capacity) {
this->expand(this->capacity+10);//扩容
}
//移动元素
for (int i = this->size - 1; i >= pos; i--) {
this->pAddr[i + 1] = this->pAddr[i];
}
this->pAddr[pos] = value;
this->size++;
}
先判断传入位置的有效性,若数组满了则扩容,之后将该位置往后的所有元素向后移动一个位置,
再将该位置赋上新添的元素,容纳数加一
2.7、按位置删除元素
void erase(int pos) { //按位置删除
if (pos < 0 || pos >= this->size) {
return;
}
for (int i = pos; i < this->size - 1; i++) {
this->pAddr[i] = this->pAddr[i + 1];
}
this->size--;
}
也是一样,先判断传入位置的有效性,再将该位置往后的所有元素向前挪动一个位置,此时被删除的元素被后面的元素所覆盖掉了,容纳数减一
2.8、元素查询
int find(T value) { //元素查询
for (int i = 0; i < this->size; i++) {
if (this->pAddr[i] == value) {
return i;
}
}
return -1;
}
遍历数组,如果查询到了,返回下标索引,循环结束还没查询到,返回-1
2.9、打印数组
void print() { //打印
for (int i = 0; i < this->size; i++) {
cout << this->pAddr[i] << ' ';
}
cout << endl;
}
遍历数组,打印每个元素值
2.10、析构
~DARR() {
delete[] this->pAddr;
this->pAddr = nullptr;
}
回收维护数组的指针指向的首地址,并将该指针指向nullptr
2.11、测试
DARR<int> arr;
//往容器内注入数据
for (int i = 0; i < 11; i++) {
arr.PushBack(i);
}
arr.print();
//测试:
//find
cout << arr.find(8) << endl;
//erase
arr.erase(0);
arr.print();
//PopBack
arr.PopBack();
arr.print();
//Insert
arr.Insert(4, 100);
arr.print();
经过测试,所有功能都能正常工作
2.12、完整代码
#include<iostream>
using namespace std;
/*
动态数组的搭建:
*/
template<class T>
class DARR
{
public:
DARR(int sizeArr = 10) :size(0), capacity(sizeArr) {
this->pAddr = new T[sizeArr];
}
//末尾插入
void PushBack(T value) {
if (this->size == this->capacity) {
this->expand(this->capacity+1);//扩容
}
this->pAddr[this->size] = value;
this->size++;
}
//末尾删除
void PopBack() {
if (this->size == 0) {
return;
}
this->size--;
}
//按位置增加元素
void Insert(int pos, T value) {
if (pos<0 || pos>this->size) {
return;
}
if (this->size == this->capacity) {
this->expand(2 * this->capacity);//扩容两倍
}
//移动元素
for (int i = this->size - 1; i >= pos; i--) {
this->pAddr[i + 1] = this->pAddr[i];
}
this->pAddr[pos] = value;
this->size++;
}
//按位置删除
void erase(int pos) {
if (pos < 0 || pos >= this->size) {
return;
}
for (int i = pos; i < this->size - 1; i++) {
this->pAddr[i] = this->pAddr[i + 1];
}
this->size--;
}
//元素查询
int find(T value) {
for (int i = 0; i < this->size; i++) {
if (this->pAddr[i] == value) {
return i;
}
}
return -1;
}
//打印
void print() {
for (int i = 0; i < this->size; i++) {
cout << this->pAddr[i] << ' ';
}
cout << endl;
}
~DARR() {
delete[] this->pAddr;
this->pAddr = nullptr;
}
private:
//扩容
void expand(int size) {
T* p = new T[size];
memcpy(p, this->pAddr, sizeof(T) * this->capacity);
delete[] this->pAddr;
this->pAddr = p;
this->capacity = size;
}
T* pAddr;//存放数据的地址
int size;//当前有多少个元素
int capacity;//容纳的最大数
};
int main() {
DARR<int> arr;
//往容器内注入数据
for (int i = 0; i < 11; i++) {
arr.PushBack(i);
}
arr.print();
//测试:
//find
cout << arr.find(8) << endl;
//erase
arr.erase(0);
arr.print();
//PopBack
arr.PopBack();
arr.print();
//Insert
arr.Insert(4, 100);
arr.print();
system("pause");
return 0;
}
3.相关算法
3.1奇偶数位置调整
问题描述:给定一个数组,将所有偶数放前面,奇数放后面
实现代码:
void AdjustArr(int arr[], int size) {
int* p = arr;
int* q = arr + size - 1;
while (p < q) {
while (p < q) {
if (*p % 2 == 1) {
break;
}
p++;
}
while (p < q) {
if (*q % 2 == 0) {
break;
}
q--;
}
if (p < q) {
int temp = *p;
*p = *q;
*q = temp;
p++;
q--;
}
}
}
创建两个指针,p指向首元素,q指向尾元素
int* p = arr;
int* q = arr + size - 1;
p从左往右找奇数,找到才退出循环
while (p < q) {
if (*p % 2 == 1) {
break;
}
p++;
}
q从右往左找偶数,找到才退出循环
while (p < q) {
if (*q % 2 == 0) {
break;
}
q--;
}
此时p是奇数,q是偶数,交换二则位置
if (p < q) {
int temp = *p;
*p = *q;
*q = temp;
p++;
q--;
}
循环上述过程,直到p越过了q,循环结束,最终所有的偶数在前面,奇数在后面
3.2移除元素问题
问题描述:给定一个数组和一个值(value),你需要原地(使用O(1)的空间复杂度),移除值为value的元素,元素顺序可以改变,返回新数组的首地址
实现代码:
int* RemoveValue(int* arr, int arrSize, int value) {
int p = 0;//找不是value的值
int q = arrSize - 1;//找value值
while (p < q) {
while (p < q) {
if (arr[p] != value) {
break;
}
p++;
}
while (p < q) {
if (arr[q] == value) {
break;
}
q--;
}
if (p < q) {
//p、q都找到后将符合value移除的放到前面
arr[q] = arr[p];
p++;
q--;
}
}
return arr + p;
}
问题分析
创建两个变量,p等于数组首索引值,q等于数组尾索引值
int p = 0;//找不是value的值
int q = arrSize - 1;//找value值
p从前往后找不是value的元素,q从后往前找值是value的元素
while (p < q) {
if (arr[p] != value) {
break;
}
p++;
}
while (p < q) {
if (arr[q] == value) {
break;
}
q--;
}
此时此刻,arr[p] != value,arr[q] == value,交换二则位置
if (p < q) {
//p、q都找到后将符合value移除的放到前面
arr[q] = arr[p];
p++;
q--;
}
循环以上过程,直到p越过了q,循环结束,最终所有值为value的元素在数组的最左边,p地址往后的元素里就没有值为value的元素了,返回该地址
测试:
int arr[] = { 1, 7, 6, 8, 6, 7, 4, 7, 11, 85, 23, 7, 5};
for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
cout << arr[i]<<' ';
}
cout << endl;
int* arr2 = RemoveValue(arr, sizeof(arr) / sizeof(int), 7);
for (int i = 0; i < 9; i++) {
cout << arr2[i] << ' ';
}
cout << endl;
经过测试,该功能符合题目要求
3.3元素逆序
问题描述:给定一个数组,将该数组里元素逆序
实现代码:
void Reverse(char str[], int size) {
char* p = str;
char* q = str + size - 1;
while (p < q) {
char temp = *p;
*p = *q;
*q = temp;
p++;
q--;
}
}
问题分析
创建两个指针,p指向首地址,q指向数组尾元素地址,交换二者的值,之后,p向右移动一个元素位置,q向左移动一个元素位置,循环上述过程,直到p越过了q,循环结束,此时完成了逆序