本文就数组、链表、队列、栈(顺序容器)进行常识性的介绍和总结,文章先分别介绍各部分知识点,最后进行对比总结。
一、 数组
用C和C++实现数组的方式是有区别的;
C通过:类型 数组名【数组大小】 显示定义数组的类型和大小,并且根据内置类型初始化规则进行初始化.比如int a[10]定义a是含有10个元素的整型数组,int默认初始化为0.用C实现的数组大小不能动态实现,如果在实际应用中数组容量不够,必须通过定义一个更大的数组将初始数组复制过去,再将小的数组删除,(通过malloc()和free()函数进行内存的分配和释放)因此在使用时通常将数组定义成足够大,所以会存在内存空间浪费的现象。
C++通过:STL标准库vector来实现,以vector 定义数组,可以指定数组的类型,根据type的类型来选择是利用内置类型、符合类型或提供了默认构造函数的类类型来进行初始化,vector容器有容量和长度之分,通常容量>长度,并且可以根据vecotor类的接口函数进行复制、赋值、访问、重新设定数组大小等操作,数组的大小可以动态实现。(通过new和delete操作符进行内存的分配和释放)。由于数组在内存当中是连续存储的,因此可以利用下标进行索引,随机访问很方便。
测试程序:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int a[10]={1,2,3};
cout<<"数组a:"<<endl;
for(int i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
vector<int> b(10);//b(10)初始化前10个元素为0
b.push_back(1);
b.push_back(2);
b.push_back(3);
cout<<"数组b的长度:"<<b.size()<<endl;
cout<<"数组b的容量:"<<b.capacity()<<endl;
cout<<"数组b:"<<endl;
for(vector<int>::iterator iter=b.begin();iter != b.end();iter++)
cout<<*iter<<" ";
cout<<endl;
system("pause");
return 0;
}
二、 链表
数组实现了数据的顺序存储,便于元素的随机访问,但是不利于删除和插入元素,并且容易造成内存空间的浪费,链表解决数组的这些缺点,内存空间利用率高,并且易于删除和插入,但是不利于随机访问,只能通过遍历链表来实现随机访问,效率不高。C++使用STL中的List类定义的方法来实现链表的各项操作。
链表从逻辑结构上分为单向链表和双向链表和循环链表。
下面结合《程序员面试宝典-3》中的相关例题进行链表分析:
1.单链表:实现单链表的建立、测长、打印、插入、删除、排序功能。
//实现单链表的建立、测长、打印、插入、删除、排序、逆置功能。
#include <iostream>
using namespace std;
typedef struct student
{
int data;
struct student *next;
}node;
//链表的建立
node *creat()
{
node *head,*p,*s;//head用来做返回值,p创建链表的移动指针,s用于添加元素时创建结点
head= new node;
p=head;
int x;
int cycle=1;//用户判定是否继续输入,0停止建立链表
while(cycle)
{
cout<<"please input the data:"<<endl;
cin>>x;
if(x!=0)
{
s=new node;
s->data=x;
p->next=s;
p=s;
}
else cycle=0;
}
head=head->next;
p->next=NULL;
return head;
}
//链表的测长
int length(node *head)
{
int n=0;
node *p=head;
while(p!=NULL)
{
p=p->next;
n++;
}
return n;
}
//链表的打印
void print(node *head)
{
node *p=head;
while(p!=NULL)
{
cout<<p->data<<" ";
p=p->next;
}
cout<<endl;
}
//链表的插入
node *insert(node *head,int num)
{
node *p0=new node;
node *p1=head;
node *p2;
p0->data=num;
if(p0->data<=head->data)
{
p0->next=p1;
head=p0;
}
else //if(p0->data>head->data)
{
while(p0->data>p1->data && p1->next !=NULL)
{
p2=p1;
p1=p1->next;
}
if(p1->next !=NULL || p0->data <=p1->data ) //还没到末尾
{
p2->next=p0;
p0->next=p1;
}
else //末尾
{
p1->next=p0;
p0->next=NULL;
}
}
return head;
}
//链表的删除
node *del(node *head,int num)
{
node *p1=head;
node *p2;
if(num==p1->data)
{
head=p1->next;
delete p1;
}
else
{
while(num!=p1->data && p1->next !=NULL)
{
p2=p1;
p1=p1->next;
}
if(num==p1->data )
{
p2->next=p1->next;
delete p1;
}
else
cout<<"该链表没有元素:"<<num<<endl;
}
return head;
}
//链表的排序
node *sort(node *head)
{
node *p0;
int n=length(head);
int temp;
if(head==NULL || head->next==NULL)
return head;
p0=head;
for(int i=1;i<n;i++)
{
p0=head;
for(int j=0;j<n-i;j++)
{
if(p0->data>p0->next->data)
{
temp=p0->next->data;
p0->next->data=p0->data;
p0->data=temp;
}
p0=p0->next;
}
}
return head;
}
//链表的逆置
node *reverse(node *head)
{
node *p1,*p2,*p3;
if(head==NULL || head->next==NULL)
return head;
p1=head,p2=p1->next;
while(p2)
{
p3=p2->next;
p2->next=p1;
p1=p2;
p2=p3;
}
head->next=NULL;
head=p1;
return head;
}
int main()
{
node *list;
int leng;
cout<<"建立链表:"<<endl;
list=creat();
list=insert(list,4);
list=del(list,1);
leng=length(list);
cout<<"建立的链表长度为:"<<leng<<endl;
cout<<"打印链表:"<<endl;
print(list);
sort(list);
cout<<"排序后打印链表:"<<endl;
print(list);
list=reverse(list);
cout<<"逆置后打印链表:"<<endl;
print(list);
system("pause");
return 0;
}
2.双链表:建立、测长、打印、删除、插入
//实现双向链表的建立、删除和插入
#include <iostream>
using namespace std;
typedef struct student
{
int data;
struct student *next;
struct student *prev;
}dnode;
//建立链表
dnode *creat()
{
dnode *head,*p,*s;
int x,cycle=1;
head=new dnode;
p=head;
while(cycle)
{
cout<<"please enter the data:";
cin>>x;
//cout<<endl;
if(x!=0)
{
s=new dnode;
s->data=x;
p->next=s;
s->prev=p;
p=s;
}
else
cycle=0;
}
p->next=NULL;
head=head->next;
head->prev=NULL;
return head;
}
//链表测长
int length(dnode *head)
{
int n=0;
dnode *p=head;
while(p!=NULL)
{
p=p->next;
n++;
}
return n;
}
//链表的打印
void print(dnode *head)
{
dnode *p=head;
while(p!=NULL)
{
cout<<p->data<<" ";
p=p->next;
}
cout<<endl;
}
//删除链表结点
dnode *del(dnode *head,int num)
{
dnode *p=head;
while(num !=p->data && p->next !=NULL)
{
p=p->next;
}
if(num==p->data)
{
if(p==head)
{
head=head->next;
head->prev=NULL;
delete p;
}
else if(p->next==NULL)
{
p->prev->next=NULL;
delete p;
}
else
{
p->prev->next=p->next;
p->next->prev=p->prev;
delete p;
}
}
else
cout<<"该链表中没有要删除的元素:"<<endl;
return head;
}
//插入链表结点
dnode *insert(dnode *head,int num)
{
dnode *p=head,*s;
s=new dnode;
s->data=num;
while(num>p->data && p->next !=NULL)
{
p=p->next;
}
if(num<=p->data)
{
if(p==head)
{
p->prev=s;
s->next=p;
head=s;
}
else
{
p->prev->next=s;
s->next=p;
s->prev=p->prev;
p->prev=s;
}
}
else
{
p->next=s;
s->prev=p;
s->next=NULL;
}
return head;
}
int main()
{
dnode *dlist;
int leng;
dlist=creat();
leng=length(dlist);
cout<<"链表长度为:"<<leng<<endl;
cout<<"打印链表:"<<endl;
print(dlist);
dlist=del(dlist,4);
cout<<"删除元素后打印链表:"<<endl;
print(dlist);
dlist=insert(dlist,5);
cout<<"插入元素后打印链表:"<<endl;
print(dlist);
//cout<<""<<endl;
system("pause");
return 0;
}
3.循环链表不做详细介绍。
三、 数组与链表的区别
1.从逻辑结构上看:数组必须预先定义好固定的长度大小,不能动态改变内存大小,当数据增加超过数组容量时必须重新定义一个更大的数组,并将原始数组复制过去,当数据减小时,数组大小并不会减小,因此数组的内存利用率低且不灵活。链表能通过new和delete等操作符动态进行分配内存,且很方便的插入和删除数据项。
2.从内存存储看:数组从栈中分配空间,因此空间的分配和回收由编译器完成,程序员方便,但自由度小。链表从堆中分配空间,空间的分配和释放必须由程序员管理。
3.从访问方式看:数组是顺序连续存储,可以利用下标进行访问,随机访问很方便,但是插入删除工作量很大。链表是链式存储结构,便于插入删除,但是访问效率比数组低。
四、 队列
先进先出FIFO,主要用于文件服务器,排队理论。
五、 栈
是后进先出LIFO表,主要用在平衡符号,后缀表达式,函数调用,中缀到后缀的转变等方面。