C++數據結構與算法從入門到放棄(一):數組(圖解+代碼)

#寫在最前
這裏是你的學渣:)
忽然發現數據結構忘得差不多了,從頭開始來一遍,就當是複習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 // 內部變量,記錄當前數組元素的個數

數組提供的接口如下:

  1. SIZE()
    返回數組元素個數。

  2. EMPTY()
    測試數組是否爲空

  3. ORDERED()
    測試數組元素是否按照升序排列。
    僞代碼:

ORDERED()
    for i <-1 upto size
      if A[i - 1] > A[i]
          return FALSE
      return TRUE
  1. GET(pos)
    獲取數組中特定位置的值,pose是元素下標。

  2. SET(pos,e)
    設置數組特定位置的值,pose是元素下標,e是新的元素值。

  3. FIND(e)
    查找數組中是否包含特定元素,若包含則返回元素下標,否則返回NotFound。e爲待查找的元素值。
    僞代碼:

FIND(e)
    for i <- 0 upto size
        if A[i] == e
            return i
        return Notfound
  1. 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 
  1. REMOVE(pos)
    刪除數組特定位置的元素,pos爲待刪除的元素下標。
  2. INSERT(pos,e)
    在數組的特定位置插入元素,pos爲待插入的元素下標,e爲待插入元素的值
  3. 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!在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章