#寫在最前
這裏是你的學渣:)
忽然發現數據結構忘得差不多了,從頭開始來一遍,就當是複習QAQ
一、數組的定義
若集合S由n個元素,且各元素之間具有一個線性次序,則可將他們存放於起始地址A、物理位置連續的一段存儲空間,並統稱作數組(array)。通常以A作爲該數組的標識,數組A[]中的每一個元素都唯一對應於某一個下標編號。在絕大多數高級程序設計語言中,一般都是從0開始編號。數組可以表示爲:
A = {a1,a2, ...,an} ,或者: A[1, n] = {A[1], A[2], ..., A[n]}
數組是一種線性表數據結構(數據排成像一條線一樣的結構。每個線性表上的數據最多隻有前和後兩個方向。)。它用一組連續的內存空間,來存儲一組具有相同類型的數據。
這樣的結構既有好處也有壞處,好處就是能實現“隨機訪問”,數組可以快速訪問數組中的任意一個數據;壞處就是在進行增刪等操作時非常低效,需要移動大量的數據來保證數據的連續性。
二、數組的訪問
前面介紹了,數組是用一組連續的內存空間來存儲數據的,那麼數組是如何實現根據下標隨機訪問數組元素的呢?
在邏輯上相鄰的元素在物理存儲上也是相鄰的。數組元素可以直接訪問:從數組的起始位置A出發,通過一次乘法運算和一次加法運算,便可以得到元素的物理地址。假設數組每個元素佔用s個單位空間(如int類型的大小爲4個字節),則元素A[i]對應的物理地址爲:
A + (i - 1)* s
數組元素的訪問操作可以在常時間內完成,所以數組比其他線性結構擁有更高的元素訪問效率。
來舉個例子吧,創建一個一個長度爲5的int類型的數組 int[]a=new int[5]
在這個圖中,計算機給數組a[5]分配了一塊連續內存空間1000~1019,其中內存塊的首地址爲A = 1000
計算機會給每個內存單元分配一個地址,計算機通過地址來訪問內存中的數據。當計算機需要隨機訪問數組中的某個元素時,它會首先通過說到的尋址公式 A + (i - 1)* s,計算出該元素存儲的內存地址。
三、數組接口
Object:
A // 內部變量,存數組元素的數組
size // 內部變量,記錄當前數組元素的個數
數組提供的接口如下:
-
SIZE()
返回數組元素個數。 -
EMPTY()
測試數組是否爲空 -
ORDERED()
測試數組元素是否按照升序排列。
僞代碼:
ORDERED()
for i <-1 upto size
if A[i - 1] > A[i]
return FALSE
return TRUE
-
GET(pos)
獲取數組中特定位置的值,pose是元素下標。 -
SET(pos,e)
設置數組特定位置的值,pose是元素下標,e是新的元素值。 -
FIND(e)
查找數組中是否包含特定元素,若包含則返回元素下標,否則返回NotFound。e爲待查找的元素值。
僞代碼:
FIND(e)
for i <- 0 upto size
if A[i] == e
return i
return Notfound
- BINARY-SEARCH(e)
使用二叉搜索查找數組中是否包含特定元素,若包含則返回元素下標,否則返回Notfound,e爲待查找的元素值。
僞代碼:
BINARY-SEARCH(e)
if ORDERED() == FALSE
error "array unsorted"
low <- 0
high <- size - 1
while low <= high
mid <- (low + high) / 2
if A[mid] > e
high <- mid -1
elseif A[mid] < e
low <- mid + 1
else
return mid
return Notfound
- REMOVE(pos)
刪除數組特定位置的元素,pos爲待刪除的元素下標。 - INSERT(pos,e)
在數組的特定位置插入元素,pos爲待插入的元素下標,e爲待插入元素的值 - SORT()
對數組的元素進行排序,使用冒泡排序。
僞代碼:
SORT()
for i <- 0 upto size-1
ordered <- TRUE
for j <- 0 upto size-1-i
if A[j] > A[j+1]
exchange A[j] with A[j+1]
ordered <- FALSE
if ordered == TRUE
break
11. SHUFFLE()
洗牌操作,打亂數組中元素的順序。
僞代碼:
SHUFFLE()
for i <- size downto 1
pos <- rand() % i
exchange A[i-1] with A[pos]
四、在C++上實現
接下來就是大家最關心的問題之如何在C++上實現,這裏我們用面向對象的方法,來創建一個數組類。直接上菜:
//Array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <stdlib.h> // standard library標準庫頭文件
#define DEFAULT_CAPACITY 10 // 宏定義數組的容量
class Array
{
private: //internal data
int* elements; //數組指針
int capacity; //數組容量
int size; //數組實際大小
private: //internal function
void expand(); //增加容量
void shrink(); //縮小容量
void exchange(int a, int b); //交換下標爲a、b兩個元素之間的位置
public:
/*三種不同的構造函數*/
Array(int c = DEFAULT_CAPACITY);
Array(int const* A, int size);
Array(const Array& v);
~Array(); //析構函數
int& operator[] (int i)const; //運算符[]重載
int getsize()const; //返回size值
bool is_empty()const; //判斷是否爲空
bool is_ordered()const; //判斷是否是升序
int find(int e)const; //返回指定值的元素值
int binary_search(int e)const; //二分搜索
int remove(int low, int high); //移除特定位置的元素
int insert(int pos, int e); //在特定位置插入元素
void push_back(int e); //將元素放到數組最後面
int pop_back(); //彈出最後一個元素
void sort(); //冒泡排序
void shuffle(); //洗牌操作,打亂順序
};
#endif
這樣我們就完成了類內聲明啦,接下來在類外定義成員函數:
//Array.cpp
#include "Array.h"
void Array::expand() //增加容量
{
capacity = capacity * 2;
int* old_elements = elements;
elements = new int[capacity];
for (int i = 0; i < size; i++)
{
elements[i] = old_elements[i];
}
delete[] old_elements;
}
void Array::shrink() //縮小容量
{
capacity = capacity / 2;
int* old_elements = elements;
elements = new int[capacity];
for (int i = 0; i < size; i++)
{
elements[i] = old_elements[i];
}
delete[] old_elements;
}
void Array::exchange(int a, int b) //交換下標爲a、b兩個元素之間的位置
{
int tmp = elements[a];
elements[a] = elements[b];
elements[b] = tmp;
}
int Array::find(int e)const //查找指定元素
{
for (int i = 0; i < size; i++)
{
if (elements[i] = e)
return i;
}
return -1;
}
/*--------------------------------------------------------------------*/
Array::Array(int c) //生成空數組
{
capacity = c;
size = 0;
elements = new int[capacity];
elements[0] = 0;
}
Array::Array(int const* A, int Size) //拷貝參數A中size範圍內的的元素
{
size = Size;
capacity = size + 1;
elements = new int[capacity];
for (int i = 0; i < size; i++)
{
elements[i] = A[i];
}
}
Array::Array(const Array& v) //拷貝數組v
{
size = v.getsize();
capacity = v.getsize() + 1;
elements = new int[capacity];
for (int i = 0; i < v.getsize(); i++)
{
elements[i] = v[i];
}
}
Array::~Array() //析構函數
{
delete[] elements;
}
int& Array::operator[] (int i)const
{
return elements[i];
}
int Array::getsize()const //返回size值
{
return size;
}
bool Array::is_empty()const //判斷是否爲空
{
return size == 0;
}
bool Array::is_ordered()const //判斷是否是升序
{
for (int i = 1; i < size; i++)
{
if (elements[i - 1] > elements[i])
return false;
}
return true;
}
int Array::binary_search(int e)const //二分搜索
{
if (is_ordered() == false)
{
return -1;
}
int low = 0;
int high = size - 1;
while (low <= high)
{
int mid = (low + high) / 2;
if (mid < e)
{
low = mid + 1;
}
else if (mid > e)
{
high = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
int Array::remove(int low, int high) //移除特定位置的元素
{
if (high > size - 1)
high = size - 1;
int delta = high + 1 - low; //delta代表要移除元素的個數
for (int i = high + 1; i < size; i++)
{
elements[i - delta] = elements[i]; //如果無法理解就想象要刪除兩個相鄰的元素的話需要怎麼操作
}
size = size - delta;
if (size < capacity / 2)
shrink();
return delta;
}
int Array::insert(int pos, int e) //在特定位置插入元素
{
if (pos > size)
pos = size;
if (size + 1 > capacity)
expand();
for (int i = size - 1; i >= pos; i--)
{
elements[i + 1] = elements[i];
}
elements[pos] = e;
size++;
return pos;
}
void Array::push_back(int e) //將元素放到數組最後面
{
insert(size, e);
}
int Array::pop_back() //彈出最後一個元素
{
int last = elements[size - 1];
remove(size - 1, size - 1);
return last;
}
void Array::sort() //冒泡排序,如果沒看懂就去看看前面的動圖
{
for (int i = 0; i < size - 1; i++)
{
bool ordered = true;
for (int j = 0; j < size - 1 - i; j++)
{
if (elements[j] > elements[j + 1])
{
exchange(j, j + 1);
ordered = false;
}
}
if (ordered == true)
break;
}
}
void Array::shuffle() //洗牌操作,打亂順序
{
for (int i = size; i > 0; i--)
{
exchange(i - 1, rand() % i); //rand() % i會產生一個0 - (i-1)的隨機數
}
}
那麼接下來就寫一個主函數測試一下部分功能:
//main.cpp
#include <iostream>
#include "Array.h"
using namespace std;
int main()
{
int G[5] = { 5,4,3,2,1 };
//三種不同方法來創建實例
Array A(G, 5);
Array B(DEFAULT_CAPACITY);
Array C(A);
for (int i = 0; i < A.getsize(); i++) //打印數組A的全部元素
{
cout << A.operator[](i) << " ";
}
cout << endl;
for (int i = 0; i < C.getsize(); i++) //打印數組C的全部元素
{
cout << C.operator[](i) << " ";
}
cout << endl;
A.sort(); //冒泡排序的函數
for (int i = 0; i < A.getsize(); i++)
{
cout << A.operator[](i) << " ";
}
cout << endl;
return 0;
}
預期的結果應該是:
A={5, 4, 3, 2, 1}, size = 5,capacity = 6;
B={}, size = 0,capacity = 10;
C={5, 4, 3, 2, 1}, size = 5,capacity = 6;
排序之後的A應該爲{1, 2, 3, 4, 5}
插入斷點調試,可以看到:
再看看輸出:
與預期結果一致。那麼其他函數我就不一一測試了,有興趣可以自行嘗試。
那麼就到這裏爲止啦,每個字都是親手敲的,希望能夠幫上忙~peace!