【数据结构与算法】dsa之C++实现向量代码Vector
一、尊敬的邓公的课程网址
学好《数据结构与算法·C++》-清华大学邓俊辉老师。所有的课件视频资料都在这里数据结构(C ++语言版),包括:
- 课程书籍
- 习题集
- dsacpp即,源代码
- 讲义、课件
- 甚至提交作业的网站
向量数据结构的实现
以下代码全为学习后手码,包括但不限于:
- 有序向量
- 无序向量
- 二分查找
- 斐波那契查找
- 起泡排序
- 归并排序
- 选择排序
1. Vecter.h头文件
之前文件名错写成Vecter,应该是Vector,代码中已经改正
#pragma once
typedef int Rank;//秩
#define DEFAULT_CAPACITY 8//设置默认容量为3
template <class T> class Vector//向量模板类
{
public:
//构造函数
Vector(int c = DEFAULT_CAPACITY, int s = 0, T v = 0) {//容量为c、规模为s、所有元素初始为v
_elem = new T[_capacity = c]; for (_size = 0; _size < s; _elem[_size++] = v);}
Vector(T const* A, Rank n) { copyFrom(A, 0, n); }//数组整体A[0,n]复制
Vector(T const* A, Rank lo, Rank hi) { copyFrom(A, lo, hi); }//数组区间复制
Vector(Vector<T> const& V) { copyFrom(V._elem, 0, V._size); }//向量整体复制
//析构函数
~Vector() { delete [] _elem; };//释放数组(内部)空间
//只读访问接口
Rank size() const{ return _size; }//规模
bool empty() const{ return !_size; }//判空
int disordered() const;//判断向量是否已经排序
Rank find(T const& e) const { return find(e, 0, _size); }//无序向量整体查找
Rank find(T const& e, Rank lo, Rank hi) const;//无序向量区间查找
Rank search(T const& e) const { return (0>=_size) ? -1 : search(e, 0, _size); } // 有序向量整体查找,search的特殊语义约定:返回不大于e且秩最大的元素
Rank search(T const& e, Rank lo, Rank hi) const;//有序向量区间查找
//可写访问接口
T& operator[](Rank r)const { return _elem[r]; }//重载下标操作符,可以类似与数组形式引用个元素
Vector<T>& operator=(Vector<T> const&);//重载赋值操作符,以便直接克隆向量
T remove(Rank r);//删除秩为r的元素
int remove(Rank lo, Rank hi);//删除秩在区间[lo,hi)之内的元素
Rank insert(Rank r, T const& e);//插入元素
Rank insert(T const& e) { return insert(_size, e); }//默认作为末元素插入
void sort(Rank lo, Rank hi);//对[lo,hi)排序
void sort() { sort(0, _size); }//整体排序
void unsort(Rank lo, Rank hi);//对[lo,hi)置乱
void unsort() { unsort(0, _size); };//整体置乱
int deduplicate();//无序去重
int uniquify();//有序去重
//遍历
void traverse(void (*visit)(T&));//遍历:使用函数指针,只读或者局部性修改
template<typename VST> void traverse(VST&);//遍历(使用函数对象,可全局性修改)
protected:
Rank _size; int _capacity; T* _elem;//向量Vector的规模、容量、数据区
void copyFrom(T const* A, Rank lo, Rank hi);//复制数组区间A[lo,hi]
void expand();//空间不足时扩容
void shrink();//装填因子过小时压缩
bool bubble(Rank lo, Rank hi);//扫描交换
void bubbleSort(Rank lo, Rank hi);//起泡排序算法
Rank max(Rank lo, Rank hi);//选取最大值
void selectionSort(Rank lo, Rank hi);//选择排序
void merge(Rank lo, Rank mi, Rank hi);//归并算法
void mergeSort(Rank lo, Rank hi);//归并排序
Rank partition(Rank lo, Rank hi);//轴点构造算法
void quickSort(Rank lo, Rank hi);//快速排序算法
void heapSort(Rank lo, Rank hi);//堆排序
};//Vector
2.斐波那契头文件
斐波那契数列头文件,仅仅是斐波那契二分查找下的黄金分割算法需要这个文件。
#pragma once
class Fib { //Fibonacci数列类
private:
int f, g; //f = fib(k - 1), g = fib(k)。均为int型,很快就会数值溢出
public:
Fib(int n) //初始化为不小于n的最小Fibonacci项
{
f = 1; g = 0; while (g < n) next();
} //fib(-1), fib(0),O(log_phi(n))时间
int get() { return g; } //获取当前Fibonacci项,O(1)时间
int next() { g += f; f = g - f; return g; } //转至下一Fibonacci项,O(1)时间
int prev() { f = g - f; g -= f; return g; } //转至上一Fibonacci项,O(1)时间
};
3.Vector.cpp源文件
#include "Vecter.h"
#include <iostream>
#include "Fib.h"
using namespace std;//swap,cout等函数
//判等器(对比)和比较器(比较)
template<class T> static bool lt(T* A, T* B) { lt(A, B); }
template<class T> static bool lt(T& A, T& B) { A < B; }//less than,A比B小?
template<class T> static bool eq(T* A, T* B) { eq(A, B); }
template<class T> static bool eq(T& A, T& B) { A = B; }//equal
template<class T> Vector<T>& Vector<T>::operator=(Vector<T> const& V) {
if (_elem) delete[] _elem;
copyFrom(V._elem);
return *this;//返回this指针(当前对象),以便链式赋值
}
template<class T> void Vector<T>::copyFrom(T const* A, Rank lo, Rank hi) {//基于复制的构造方法
_elem = new T[_capacity = 2 * (hi - lo)]; _size = 0;//分配两倍容量,规模清零
while (lo < hi)//约定:数组A[lo,hi)
_elem[_size++] = A[lo++];//特别注意此时的lo++是,先取Rank空间再对lo = lo+1
}
template<class T> void Vector<T>::expand() {
if ( _size < _capacity) return;//尚未满员时,无需扩容
if (_capacity < DEFAULT_CAPACITY) _capacity = DEFAULT_CAPACITY;//保障不低于最小容量
//下处的处理非常聪明的地方在于:数组的迁移,使用指针这一技巧,改变了指向。
T* oldelem = _elem; _elem = new T[_capacity<<=1]; //两倍扩容,二进制下左移一位,即为乘以2
for (int i = 0; i < _size; i++) {
_elem[i] = oldelem[i];}//复制原向量内容,或已经重载【operator=】的前提下
delete[] oldelem;//释放原空间
}
template<class T> void Vector<T>::shrink() {//装填因子过小时压缩,本代码中约定装填不足25%
if (_capacity < _capacity << 1) return; //不至于收缩到默认二倍容量以下,上界
if (_size << 2 > _capacity) return;//以25%为界,下界
T* oldelem = _elem; _elem = new T[_capacity >>= 2];//容量减半
for (int i = 0; i < _size; i++) {
_elem[i] = oldelem[i];
}
delete[] oldelem;//除了上下界,与扩容是对称的
}
template<class T> void permute(Vector<T> V) {//随机置乱向量,使各元素等概率出现于各位置
for (int i = V.size(); i > 0; i--) {
swap(V[i - 1], V[rand() % i]);//一般性:rand() % (b-a+1)+ a ;b=i-1.就表示 a~b 之间的一个随机整数。
}
}//置乱算法,算法原理但是不属于Vector
template<class T> void Vector<T>::unsort(Rank lo,Rank hi) {
T* V = _elem + lo;//把_elem看作指针,指向的是一组连续数组空间,加lo之后仅仅只有指针位置变化
//数组的内容不变,也就是此时V指向的是_elem[lo]这个元素
//V数组的真实长度变为V[0,hi-lo],内容变为_elem[lo,hi],(不过也可能比hi长)
for (int i = hi - lo; i > 0; i--) {
swap(V[i - 1],V[rand() % i]);
}
}//区间置乱接口
template <class T> Rank Vector<T>::find(T const&e, Rank lo, Rank hi) const{
while ((lo < hi--) && (e != _elem[hi]));//先判断再自减,和之前的copyFrom函数[lo++]同理
return hi; //若未找到返回lo-1
}
template <class T> Rank Vector<T>::insert(Rank r, T const& e) {
expand();
for (int i = _size; i > r; i--) _elem[i] = _elem[i - 1];//自后向前,由于C++是[lo,hi),所以_size=hi
_elem[r] = e; _size++;//置入新元素并更新容量
return r;//返回秩
}
template <class T> T Vector<T>::remove(Rank r) {
T oldelem = _elem[r];
remove(r, r + 1);
return oldelem;
}//实际可行的思路恰好相反
template <class T> int Vector<T>::remove(Rank lo, Rank hi) {
if (lo = hi)return 0;
while (hi < _size)_elem[lo++] = _elem[hi++];
_size = lo;//size= 现在的lo
shrink();
return hi - lo;
}
//唯一化,视向量是否有序,该功能有两种实现方式
//方式一:无序向量,O(n^2)
template<class T> int Vector<T>::deduplicate() {
int oldsize = _size;
Rank i = 1;
while (i<_size) (find(_elem[i], 0, i) ) < 0 ? i++ : remove(i);//666
//此步骤的证明在书本P42·图2.6中
//复杂度为n^2
//明智之举在于,只需要保证前缀中所有元素彼此互异的不变性即可
return oldsize - _size;//remove函数中改变了_size函数
}
//方式二:有序向量,O(n)
template<class T> int Vector<T>::uniquify() {
Rank i = 0, j = 0;
while (++j < _size)
if (_elem[i] != _elem[j])
_elem[++i] = _elem[j];//规律:只要是++i/i++遇到其他的运算符,用赋值运算符来记忆
//=++i,先加一再赋值;=i++,先赋值再加一
//结论:++前缀先算,++后缀后算
_size = ++i; shrink();
return j - i;
}
//遍历:将向量看作一个整体,对其中所有元素实施某种统一的操作
//例如:输出向量中所有的元素,或者按照某种运算流程统一修改所有元素的数值
//方式一:《DSA·第三版》P43,没太懂
template<class T> void Vector<T>::traverse(void (*visit)(T&)) {//借助函数指针机制
for (int i = 0; i < _size; i++) visit(_elem[i]);}
//方式二:《DSA·第三版》P43,没太懂
template<typename T> template<typename VST>//元素类型、操作器
void Vector<T>::traverse(VST& visit)
{ for (int i = 0; i < _size; i++) visit(_elem[i]); }
template<class T> int Vector<T>::disordered() const {//有序性甄别
int n = 0;//计数器
for (int i = 1; i < _size; i++) if (_elem[i-1] > _elem[i]) n++;//从i=1开始,防止越界
return n;
}
二分查找,版本A
//template<class T> Rank binSearch(T &_elem, T e, Rank lo, Rank hi) {
// while (lo<hi){
// Rank mi = (lo + hi) >> 1;
// if (e < _elem[mi]) hi = mi;
// else if (_elem[mi] < e) lo = mi + 1;
// else return mi;}
// return -1;//无法严格按照语义执行
//}
二分查找,版本B
//template<class T> Rank binSearch(T& A, T e, Rank lo, Rank hi) {
// while (1 < hi - lo) {
// Rank mi = (hi + lo) >> 1;//以中点为轴点,因此是+号
// (e < A[mi] )? hi = mi : lo = mi;}
// return ( A[lo] == e )? lo : -1;//无法严格按照语义执行
//}
//二分查找,版本C
template<class T> Rank binSearch(T& A, T e, Rank lo, Rank hi) {
while (lo < hi) {
Rank mi = (hi + lo) >> 1;
e < A[lo] ? hi = mi : lo = mi + 1;//最后一项必是 lo = mi + 1;
}
return --lo;//严格按照语义执行
}
template <class T> Rank fibSearch(T& A, T e, Rank lo, Rank hi) {
Fib fib(hi - lo);
while (lo < hi) {
while (hi - lo < fib.get()) fib.prev();
Rank mi = lo + fib.get() - 1;
//总结
//如果是初始值加区间长度,一定要减一
//如果是[lo,hi)区间,区间长度 = hi - lo
//for循环中i = 0(左闭)i<_size(右开),会循环[0,_size),区间长度 = _size,循环这么多次。
//while循环中lo<hi ==> 0<hi - lo即循环到hi = lo终止//while循环中1< hi - lo即循环到[lo,hi),hi = lo+1
//因此while循环左边是几(0,1,2……),区间长度就是几
//但是指针不同,_elem+lo,就是指向lo这个位置
(e < A[mi]) ? hi = mi : lo = mi + 1;
}
return --lo;
}
template<class T> Rank Vector<T>::search(T const& e, Rank lo, Rank hi) const {//有序向量区间查找
//严格语义:有序向量查找,返回不大于e的最后一个元素
return (rand() % 2) ? binSearch(_elem, e, lo, hi) : fibSearch(_elem, e, lo, hi);//0或1,各50%的规律。
}
template<class T> bool Vector<T>::bubble(Rank lo, Rank hi) {//一趟排序
bool sorted = true;
while (++lo < hi)
{
if (_elem[lo - 1] > _elem[lo]) {
sorted = false;
swap(_elem[lo - 1], _elem[lo]);
}
}
return sorted;}
template<class T> void Vector<T>::bubbleSort(Rank lo, Rank hi) {
while (!bubble(lo, hi--));//一定是hi--,因为后面都是有序的
//也就是每一趟的排序区间为[lo,hi--)
}
template<class T> Rank Vector<T>::max(Rank lo, Rank hi) {
Rank i = 0, j = 0;
while (++j < hi)
if (_elem[i] < _elem[j]) i = j;
return i;
}
template<class T> void Vector<T>::selectionSort(Rank lo, Rank hi) {
while (lo<hi){
Rank m= max(lo, hi);
insert(--hi, _elem[m]);}
}
//*****非常完美的合并算法*****
template<class T> void Vector<T>::merge(Rank lo, Rank mi, Rank hi) {
T* A = _elem + lo;//A = _elem[lo,hi]
int lb = mi - lo; T* B = new T[lb];
for (Rank i = 0; i < lb; B[i] = A[i++]);//B = _elem[lo, mi]
int lc = hi - mi; T* C = _elem + mi;//C = _elem[mi, hi]
for (Rank i = 0,j = 0,k = 0; (j < lb) || (k < lc);){
if ((j < lb) && (!(k < lc) || (B[j] <= C[k])))A[i++] = B[j++];
if ((k < lc) && (!(j < lb) || (C[k] < B[j])))A[i++] = C[k++];}
delete[] B;
}
template<class T> void Vector<T>::mergeSort(Rank lo, Rank hi) {
if (hi - lo < 2) return;
Rank mi = (hi + lo) >> 1;//以终点为例
mergeSort(lo, mi); mergeSort(mi,hi);
merge(lo, mi, hi);
}
template<class T> void Vector<T>::sort(Rank lo, Rank hi) {
switch (rand() % 5) {
case 1:bubbleSort(lo,hi); break;//起泡排序
case 2:selectionSort(lo, hi); break;//选择排序
case 3:mergeSort(lo, hi); break;//归并排序
//case 4:heapSort(lo, hi); break;//堆排序
//default:quickSort(lo, hi); break;//快速排序
}//快速排序
}
void main() {
int A[5] = { 1, 2, 30, 45, 6 };
Vector<int> test(A,0,5);
test.sort(0,5);
Rank a = test.find(5);
cout << "**************************\n";
cout << "\n";
cout << "坚持,很强,又一次成功运行\n";
cout << "\n";
cout << "**************************\n";
cout << "A+lo:"<<A+1 << "\n";
for (int i = 0; i <5; cout << "i:" << i << " A[i++]:" << A[i++]<< " rand()" << rand()<<" rand() % i:" <<rand() % i << "\n");
}