赵振江
数据结构
数组
一维数组
“数组”你真的很了解吗?
数组大家都不是很陌生,它已经“植入”了许多编程语言,但初学者一提到数组,可能不会联想到“数据结构”,而是想到的会是一种“数据类型”,数组本质上就是一种极其简单的数据结构。所谓数组,就是相同数据类型的元素按一定顺序排列的集合。也就是在内存中划分一段连续的且大小固定(注意是连续)的内存空间(或者其他存储器)保存相同数据类型的数据,如下图。一般说的简单数组,都是静态的数组,所以很容易导致数据溢出和访问越界。不需担心的是,很多高级语言中已经设计了动态数组了,这个以后还会讨论。
数组的维数
数组的维数始终是个重要话题,这里先看看最简单的一维数组。对了,搞数学的更愿意把二维数组称之为“矩阵”,关于矩阵的话题也很多,以后会提到。依次类推还有三维、四维、……
数组和表有什么关系
先说明一下:数组属于线性表
。关于表的话题以后再说,不急。
数组和数据类型
int
型的数组、float
型的数组好理解,也就是规定数组里面存储了统一数据类型的变量(绝大部分编程语言都是这样,有些不一定如:Visual Foxpro)。在C/C++中,数组是基本数据类型,C中作为一种构造数据类型出现,而在java中作为引用数据类型出现(有的书也称之为 对象数据类型)。
数组与Java,C++,C,JS,汇编……(之后再补充)
知道了数组的数据组织方式(在内存中的结构),接下来就要知道对于数组的一些操作(处理)了。主要就是插入insert,查找find,删除remove,显示。这里分别用几种编程语言来实现,显然java和C++等具有面向对象编程思想的编程语言,对于实现算法要直观些,但要讲到对于在数组内存中的体现,显然C和汇编直观些。
Java语言版本
class Array{
private long[] a;
int nElems;
//构造函数constructor
public Array(int size){
this.a=new long[size];
this.nElems=0;
}
//插入insert
public void insert(long e){
a[nElems]=e;
nElems++;
}
//查找find
public int find(long searchKey){
int j;
for(j=0;j<nElems;j++){
if(searchKey==a[j])
return j;
}
return -1;
}
//删除delete
public boolean remove(long searchKey){
int k =find(searchKey);
if(k!=-1){
for(;k<nElems;k++){
a[k]=a[k+1];
}
nElems--;
return true;
}
return false;
}
//显示display
public void display(){
for(int i =0;i<nElems;i++){
System.out.print(a[i]+" ");
}
}
}
public class ArrayTest {
public static void main(String[] args){
Array array = new Array(10);
//插入
array.insert(12);
array.insert(13);
array.insert(11);
array.insert(15);
array.insert(16);
//查找
if(array.find(13)!=-1)
System.out.println("查找成功,下标为:"+array.find(13));
else System.out.println("查找失败!");
//删除
if(array.remove(11))
System.out.println("删除成功!");
else System.out.println(" 删除失败!");
//显示
System.out.print("显示数组:");
array.display();
}
}
C++语言版本
#include <iostream>
using namespace std;
template <class T>
class Array{
private :
int size; //数组大小
T *a; //一维数组
int nElems; //实际元素个数
public:
Array(int sz); //构造函数
~Array() {delete [] a;} //析构函数
int Size(){ //返回数组大小
return size;
}
void insert(T e); //插入
int find(T e); //查找
bool remove(T e); //删除
void display();
};
template <class T>
Array<T>::Array(int sz){
if(sz<0){
cerr<< "数组元素不能为0"<<endl;
return;
}
else{
size=sz;
a= new T[sz];
nElems=0;
}
}
/////////插入/////////////
template <class T>
void Array<T>:: insert(T e){
a[nElems]=e;
nElems++;
}
//////////查找///////////////
template <class T>
int Array<T>::find(T searchKey){
int j;
for(j=0;j<nElems;j++){
if(searchKey==a[j])
return j;
}
return -1;
}
///////////删除///////////
template <class T>
bool Array<T>::remove(T e){
int k =find(e);
if(k!=-1){
for(;k<nElems;k++){
a[k]=a[k+1];
}
nElems--;
return true;
}
return false;
}
//////////显示//////////////
template <class T>
void Array<T>::display(){
for(int i =0;i<nElems;i++){
cout<<a[i]<<" ";
}
}
int main() {
Array<long> arr(10);
//插入
arr.insert(12);
arr.insert(13);
arr.insert(14);
arr.insert(15);
arr.insert(16);
//显示
arr.display();
cout<<endl;
//查找
cout<<arr.find(13)<<endl; //注意返回的是下标
//删除
arr.remove(14);
//显示
arr.display();
~Array();
return 0;
}
JS语言版本
//稍等两天提供
C语言版本
具体操作这里不再提供,只提供struct结构体,构造数组和销毁数组函数
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
#define ElemType int
#define Status int
#define OVERFLOW 3
//定义结构体Array
typedef struct{
ElemType *base;
int nElems;
int size;
}Array;
//初始化数组,类似于java和C++的构造函数
Status InitArray(Array A,int sz){
A.size=sz;
if(sz<0)
return ERROR;
A.base=(ElemType *)malloc(A.size*sizeof(ElemType));
if(!A.base) exit(OVERFLOW);
return OK;
}
//销毁数组,类似C++的析构函数和java的垃圾回收
Status DestroyArray(Array A){
if(!A.base)
return ERROR;
free(A.base);
A.base=NULL;
return OK;
}
Win32汇编版本
汇编版本的更加直观,所谓直观,是直接到了内存的层面。程序先是以Array为基地址,连续创建了4块每块大小为32bit(4字节,双字)连续的空间,并进行了初始赋值。下面用提示框(MessageBox)进行数组的展示,和数组大小的展示(用lengthof可以更快实现,程序中都有用到)。接着用一个loop循环,依次遍历读取数组各个元素,算出数组元素的和。其中部分注释展示了,数组的读取和删除,注意删除的时候,数组后面元素会向前移动,这在上面的Java 和C++中有所展示,汇编里就不再展示了。
.386
.model flat,stdcall
option casemap:none
include user32.inc
include windows.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.data
Array DWORD 12,23,11,22 ;Array为基地址,数据类型为双字(32bit,4个字节)
ArraySize=($-Array)/4
szCap db ‘一维数组‘,0
szText1 db 100 dup(0) ;100byte字符缓冲区
szText2 db 100 dup(0)
szOutPut1 db ‘数组:%d,%d,%d,%d 长度:%d‘,0
szOutPut2 db ‘数组:%d‘,0
.code
start:
invoke wsprintf,addr szText1,addr szOutPut1,Array,Array+4,Array+8,Array+12, ArraySize
invoke MessageBox,NULL,offset szText1,offset szCap,MB_OK
mov esi,offset Array
mov ecx,lengthof Array
L: ;循环读取数组元素,并求所有元素之和
add eax,[esi]
add esi,type Array ;指向下一个元素
loop L
;mov eax,[esi+4] ;数组第2个元素
;mov [esi+8],[esi+12] ;删除数组第三个元素
invoke wsprintf,addr szText2,addr szOutPut2,eax
invoke MessageBox,NULL,addr szText2,offset szCap,MB_OK
invoke ExitProcess,NULL
end start
数组存放对象元素
以上无论是什么语言的数组,里面存储的元素都是基本数据类型
的,这显然不符合平时的需求,我们希望它可以存储对象元素(当然,得是遵循OOP的编程语言)。这里以java为例,用数组来存放Person类类型的数据。
package array;
/**
*
* @author River 2015-1-27
*
*/
/**
* Person类
*/
class Person{
private long ID;
private String name;
private int age;
public Person(long id,String n,int a){
ID=id;
name=n;
age=a;
}
public String display(){
return "id:"+ID+" 姓名:"+name+" 年龄:"+age;
}
public String getName(){
return name;
}
public long getID(){
return ID;
}
}
/**
* ClassDataArray类实现,数组保存对象
* @author River
*
*/
class ClassDataArray{
private Person[] a; //定义一个Person类型的数组
private int nElems; //实际元素个数
public ClassDataArray(int size){
a=new Person[size];
nElems=0;
}
////////////插入/////////////////
//public void insert(Person per){
public void insert(long id,String name,int age){
a[nElems]= new Person(id, name, age);
nElems++;
}
////////////查找//////////////////
public int find(long id){
for(int j=0;j<nElems;j++){
if(a[j].getID()==id)
return j; //找到,返回下标
else if(j==nElems)
break; //没有查找到
}
return nElems;
}
///////////查找另一种写法////////////
/*public Person find2(String name){
for(int j=0;j<nElems;j++){
if(a[j].getName().equals(name))
return a[j]; //找到,返回下标
else if(j==nElems)
break; //没有查找到
}
return null;
}*/
//////////////删除//////////////
public boolean delete(long id){
if(find(id)!=nElems){
for(int j=find(id);j<nElems;j++)
a[j]=a[j+1];
nElems--;
return true;
}
return false;
}
///////////////显示/////////////////
public void display(){
for(int j=0;j<nElems;j++){
System.out.println(a[j].display());
}
}
}
public class ClassDataArrayTest {
public static void main(String[] args){
ClassDataArray arr = new ClassDataArray(10);
arr.insert(1, "River", 22);
arr.insert(2, "ShiTianShaoNv", 22);
arr.insert(3, "QiXiaoXing", 21);
arr.display();
arr.find(1);//可以写成查找名字的形式
arr.delete(2);
arr.display();
}
}
数组真的
是存储的对象?
数组确实存储的是对象元素,但可能与你心中想象的有些区别,这时,数组实际存储的是一个地址,这个地址指向目标对象在堆
里面的首地址,而这个变量名(对象名),存在于栈当中。
数组的优点与缺点
优点:数据结构简单,可以完成简单的数据存储。当对于数据元素没有要求时(比如顺序),可以很容易的进行数据添加(在数组尾部插入即可)。
缺点:数组的大小尺寸是不变的,太小容易越界,太大浪费空间。查找,添加,删除都比较费时(除去最好情况),移动数组元素比较频繁。具体的复杂度这里先不给出,下篇专门讨论复杂度问题。
数组的升级版
有序数组,放到算法分析里以其例子进行说明。