链表是一种线性数据结构,占用不连续的内存,用一个或两个指针(或元素的代号)保存与其相邻的元素。
怎么用类实现一个链表呢?(这里我们讨论双向链表)
首先,我们可以建一个数组,保存各元素,只不过元素的顺序不一定是按照下标排列的。
元素可以用一个结构体表示,它有三个元素:数值、上一个元素的指针、下一个元素的指针。
然后,要添加头指针和尾指针,以确定头和尾的位置。
接着,我们最好考虑一下各函数的实现,以免出现一些问题。
- 构造函数。构造函数可以把数组中的全部元素都用上吗?显然不能。因为这样就无法往里面插入新的元素了。只能初始化一部分或全不初始化。
- 输入/输出函数。这一部分实不实现都行,如果一定要实现,按照指针依次寻找就行了。
- 查找元素函数。根据链表的性质,它的时间复杂度最快只能是O(n)。这也一样,按照指针依次寻找就行了。
- 删除元素函数。这也很简单,把相邻元素的指针修改一下就行了。
- 插入元素函数。第一步,要找到一个不用的元素,这就需要再建一个数组,用来标记有没有用过,再加入一个循环,选择第一个不用的元素,如果全用过了,那就不插入或报错。第二步,修改相邻元素的指针。
根据以上几点,我们要再建一个数组表示是否用过。
代码如下:
#ifndef BASIC_LINKED_LIST
#define BASIC_LINKED_LIST
#include<iostream>
#include<cstdio>
using namespace std;
#ifndef MAX_SIZE
#define MAX_SIZE 1003
#endif
//macros
#ifdef FOR_LIST
#define for_list(list_name,type,ptr_name)\
list_element<type> *ptr_name=list_name.head;\
for(;p!=list_name.tail;p=p->next)
#define for_list_reverse(list_name,type,ptr_name)\
list_element<type> *ptr_name=list_name.tail;\
for(;p!=list_name.head;p=p->pre)
#endif
#ifdef DEFINE_TYPE
#define def_list_ele(type,name) typedef list_element<type> name
#define def_plist_ele(type,name) typedef list_element<type>* name
#define def_list(type,name) typedef basic_linked_list<type> name
#endif
//list_element class
template<typename T>
class list_element{
public:
T data;
list_element *next,*pre;
};
//basic_linked_list class
template<typename T>
class basic_linked_list{ //Without new/delete.The name of a linked list class with new/delete will be "linked_list".
public:
list_element<T> datas[MAX_SIZE];
bool used[MAX_SIZE];
list_element<T> *head,*tail;
//constructors
basic_linked_list(){ // WARNING: The datas in this list will be RANDOM !!! And there is NO PLACE to INSERT !!!
datas[0].next=&datas[1],datas[0].pre=0,used[0]=1,head=datas;
int stsz_cut1=MAX_SIZE-1; //st==start,sz==size,cut1=="minus 1"
for(int i=1;i<stsz_cut1;i++){
datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1],used[i]=1;
}
datas[stsz_cut1].next=0,datas[stsz_cut1].pre=&datas[stsz_cut1-1],used[stsz_cut1]=1,tail=&datas[stsz_cut1];
}
basic_linked_list(int start_size){ // WARNING: The datas in this list will be RANDOM !!!
datas[0].next=&datas[1],datas[0].pre=0,used[0]=1,head=datas;
int stsz_cut1=start_size-1; //st==start,sz==size,cut1=="minus 1"
for(int i=1;i<stsz_cut1;i++){
datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1],used[i]=1;
}
datas[stsz_cut1].next=0,datas[stsz_cut1].pre=&datas[stsz_cut1-1],used[stsz_cut1]=1,tail=&datas[stsz_cut1];
}
basic_linked_list(T start_data,int start_size){ //if start_size==5 : 0/1/2/3/4 can be used
datas[0].data=start_data,datas[0].next=&datas[1],datas[0].pre=0,used[0]=1,head=datas;
int stsz_cut1=start_size-1; //st==start,sz==size,cut1=="minus 1"
for(int i=1;i<stsz_cut1;i++){
datas[i].data=start_data,datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1],used[i]=1;
}
datas[stsz_cut1].data=start_data,datas[stsz_cut1].next=0,datas[stsz_cut1].pre=&datas[stsz_cut1-1];
used[stsz_cut1]=1,tail=&datas[stsz_cut1];
}
//inputs and outputs
//input
void input_cin(){
list_element<T> *p=head;
for(;p!=tail;p=p->next){
cin>>p->data;
}
cin>>p->data;
}
void input_scanf(const char T_str[]="%d"){
list_element<T> *p=head;
for(;p!=tail;p=p->next){
scanf(T_str,p->data);
}
scanf(T_str,p->data);
}
void input_cin(int size){
list_element<T> *p=head;
for(int i=0;i<size;i++,p=p->next){
cin>>p->data;
}
}
void input_scanf(int size,const char T_str[]="%d"){
list_element<T> *p=head;
for(int i=0;i<size;i++,p=p->next){
scanf(T_str,&p->data);
}
}
void input_cin(int begin,int end){ // [begin,end]
list_element<T> *p=head;
for(int i=0;i<begin;i++,p=p->next);
for(int i=begin;i<end;i++,p=p->next){
cin>>p->data;
}
}
void input_scanf(int begin,int end,const char T_str[]="%d"){ // [begin,end]
list_element<T> *p=head;
for(int i=0;i<begin;i++,p=p->next);
for(int i=begin;i<end;i++,p=p->next){
scanf(T_str,&p->data);
}
}
//output--from head
void output_cout(){
list_element<T> *p=head;
for(;p!=tail;p=p->next){
cout<<p->data<<" ";
}
cout<<p->data<<" ";
}
void output_printf(const char T_str[]="%d "){
list_element<T> *p=head;
for(;p!=tail;p=p->next){
printf(T_str,p->data);
}
printf(T_str,p->data);
}
void output_cout(int size){
list_element<T> *p=head;
for(int i=0;i<size;i++,p=p->next){
cout<<p->data<<" ";
}
}
void output_printf(int size,const char T_str[]="%d "){
list_element<T> *p=head;
for(int i=0;i<size;i++,p=p->next){
printf(T_str,p->data);
}
}
void output_cout(int begin,int end){ // [begin,end]
list_element<T> *p=head;
for(int i=0;i<begin;i++,p=p->next);
for(int i=begin;i<end;i++,p=p->next){
cout<<p->data<<" ";
}
}
void output_printf(int begin,int end,const char T_str[]="%d "){ // [begin,end]
list_element<T> *p=head;
for(int i=0;i<begin;i++,p=p->next);
for(int i=begin;i<end;i++,p=p->next){
printf(T_str,p->data);
}
}
//output--from tail (it is said,reverse it)
void output_cout_reverse(){
list_element<T> *p=tail;
for(;p!=head;p=p->pre){
cout<<p->data<<" ";
}
cout<<p->data<<" ";
}
void output_printf_reverse(const char T_str[]="%d "){
list_element<T> *p=tail;
for(;p!=head;p=p->pre){
printf(T_str,p->data);
}
printf(T_str,p->data);
}
void output_cout_reverse(int size){
list_element<T> *p=tail;
for(int i=0;i<size;i++,p=p->pre){
cout<<p->data<<" ";
}
}
void output_printf_reverse(int size,const char T_str[]="%d "){
list_element<T> *p=tail;
for(int i=0;i<size;i++,p=p->pre){
printf(T_str,p->data);
}
}
void output_cout_reverse(int begin,int end){ // [begin,end]
list_element<T> *p=head;
for(int i=0;i<end;i++,p=p->next); //find end
for(int i=end;i>=begin;i--,p=p->pre){
cout<<p->data<<" ";
}
}
void output_printf_reverse(int begin,int end,const char T_str[]="%d "){ // [begin,end]
list_element<T> *p=head;
for(int i=0;i<end;i++,p=p->next); //find end
for(int i=end;i>=begin;i--,p=p->pre){
printf(T_str,p->data);
}
}
//functions
bool full(){
for(int i=0;i<MAX_SIZE;i++){
if(used[i]==0) return 0;
}
return 1;
}
list_element<T>* find(int pos){ //from 0
list_element<T> *p=head;
for(int i=0;i<pos;i++,p=p->next);
return p;
}
T find_data(int pos){ //from 0
list_element<T> *p=head;
for(int i=0;i<pos;i++,p=p->next);
return p->data;
}
T insert(int pos,T new_data){ // a b c d (e)
// ^ : pos==c
list_element<T> *findpos=find(pos);
for(int i=MAX_SIZE-1;i>=0;i--){
if(!used[i]){
used[i]=1;
if(findpos==head){
datas[i].data=new_data;
datas[i].next=findpos;
head=&datas[i];
return new_data;
}
datas[i].data=new_data;
datas[i].next=findpos;
datas[i].pre=findpos->pre;
findpos->pre->next=&datas[i];
findpos->pre=&datas[i];
return new_data;
}
}
}
T insert_back(T new_data){
for(int i=MAX_SIZE-1;i>=0;i++){
if(!used[i]){
used[i]=1;
datas[i].data=new_data;
datas[i].pre=tail;
tail->next=&datas[i];
tail=&datas[i];
return new_data;
}
}
}
T erase(int pos){
used[pos]=0;
list_element<T> *findpos=find(pos);
findpos->pre->next=findpos->next;
findpos->next->pre=findpos->pre;
return findpos->data;
}
};
#ifdef TYPEDEF
typedef list_element<int> intnode;
typedef list_element<double> realnode;
typedef basic_linked_list<int> intlist;
typedef basic_linked_list<double> reallist;
#endif
#endif
这时候我们发现,插入元素本来是O(1)的,现在却变成了O(n),而且数组的大小还是确定的。
所以,可以想到,用内存申请可以解决这些问题。
用到了内存申请后,就只用判断要不要扩大了,如果数据到顶了就扩大。这样可能会浪费一点空间(删除后的元素还会占空间却不会被利用),但是插入元素函数的时间复杂度降到了O(1)。另外,拷贝构造函数和扩大空间函数也需要实现。
代码如下:
#ifndef LINKED_LIST
#define LINKED_LIST
#include<iostream>
#include<cstdio>
#include<cassert>
using namespace std;
template<typename T>
class list_node{
public:
T data;
list_node<T> *next,*pre;
};
template<typename T>
class linked_list{
public:
list_node<T> *datas;
int length,used_length,used_pos; //used_pos:for insert
list_node<T> *head,*tail;
// (copy) constructors and destructors
linked_list(){
datas=0,length=used_length=0,head=tail=0,used_pos=0;
}
linked_list(int size){ // WARNING: The datas in this pointer might be RANDOM !!!
if(size>1){
datas=new list_node<T>[size],length=used_length=size,used_pos=size;
datas[0].next=&datas[1],datas[0].pre=0,head=&datas[0];
for(int i=1;i<size-1;i++){
datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1];
}
datas[size-1].next=0,datas[size-1].pre=&datas[size-2],tail=&datas[size-1];
head->pre=0,tail->next=0;
}
else if(size==1){
datas=new list_node<T>[1],length=used_length=1,used_pos=1;
datas[0].next=0,datas[0].pre=0,head=tail=&datas[0];
head->pre=0,tail->next=0;
}
else if(size==0){
datas=0,length=used_length=0,head=tail=0,used_pos=0;
}
else assert(0);
}
linked_list(T start_data,int size){
if(size>1){
datas=new list_node<T>[size],length=used_length=size,used_pos=size;
datas[0].data=start_data,datas[0].next=&datas[1],datas[0].pre=0,head=&datas[0];
for(int i=1;i<size-1;i++){
datas[i].data=start_data,datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1];
}
datas[size-1].data=start_data,datas[size-1].next=0,datas[size-1].pre=&datas[size-2],tail=&datas[size-1];
head->pre=0,tail->next=0;
}
else if(size==1){
datas=new list_node<T>[1],length=used_length=1,used_pos=1;
datas[0].data=start_data,datas[0].next=0,datas[0].pre=0,head=tail=&datas[0];
head->pre=0,tail->next=0;
}
else if(size==0){
datas=0,length=used_length=0,head=tail=0,used_pos=0;
}
else assert(0);
}
linked_list(const linked_list &list){
if(list.length>1){
delete[] datas;
datas=new list_node<T>[list.length],length=list.length,used_pos=list.used_pos;
int headnum=list.head-list.datas,tailnum=list.tail-list.datas;
datas[0].data=list.datas[0].data,datas[0].next=&datas[list.datas[0].next-list.datas],datas[0].pre=0;
head=&datas[headnum];
for(int i=1;i<length-1;i++){
int nextnum=list.datas[i].next-list.datas,prenum=list.datas[i].pre-list.datas;
datas[i].data=list.datas[i].data,datas[i].next=&datas[nextnum],datas[i].pre=&datas[prenum];
}
datas[length-1].data=list.datas[length-1].data,datas[length-1].next=0;
tail=&datas[tailnum],datas[length-1].pre=&datas[list.datas[length-1].pre-list.datas];
head->pre=0,tail->next=0;
}
else if(list.length==1){
delete[] datas;
datas=new list_node<T>[1],length=1,used_pos=1;
datas[0].data=list.datas[0].data,datas[0].next=0,datas[0].pre=0,head=tail=&datas[0];
}
else if(list.length==0){
datas=0,length=0,head=tail=0,used_pos=0;
}
else assert(0);
}
~linked_list(){
if(datas){
delete[] datas;
datas=0;
}
}
//input and output
//functions
bool full(){
return length==used_length;
}
bool pos_full(){
return used_pos==length;
}
bool is_pos_ok(int pos){
return pos<used_length && pos>=0;
}
list_node<T>* find(int pos){
pos--;
if(pos>used_length) return 0;
list_node<T> *p=head;
for(int i=0;i<pos;i++,p=p->next);
return p;
}
T find_data(int pos){
pos--;
if(pos>used_length) assert(0);
list_node<T> *p=head;
for(int i=0;i<pos;i++,p=p->next);
return p->data;
}
T add_place(){cout<<" ADD PLACE ";
int new_length=(length==0 ? 1 : length<<1);
list_node<T> *new_datas=new list_node<T>[new_length];
int i=0;
for(;i<length;i++){
int nextnum=datas[i].next-datas,prenum=datas[i].pre-datas;
new_datas[i].data=datas[i].data,new_datas[i].next=&datas[nextnum],new_datas[i].pre=&datas[prenum];
}
int headnum=head-datas,tailnum=tail-datas;
used_pos=length,datas=new_datas,length=new_length,head=&datas[headnum],tail=&datas[tailnum];
head->pre=0,tail->next=0;
// for(;i<length-1;i++){
// datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1];
// }
// datas[length-1].next=0,datas[length-1].pre=&datas[length-2];
}
T insert(int pos,T new_data){
if(!is_pos_ok(pos)) assert("The wrong place is : in insert function,the argument \"pos\" is not ok.");
if(pos_full()) add_place();
list_node<T> *findpos=find(pos);
if(pos==0){
datas[used_pos].data=new_data;
datas[used_pos].next=findpos;
datas[used_pos].pre=0;
findpos->pre=&datas[used_pos];
head=&datas[used_pos];
}
else{
datas[used_pos].data=new_data;
datas[used_pos].next=findpos;
datas[used_pos].pre=findpos->pre;
findpos->pre->next=&datas[used_pos];
findpos->pre=&datas[used_pos];
}
used_pos++,used_length++;
}
T insert_back(T new_data){
if(pos_full()) add_place();
datas[used_pos].data=new_data;
datas[used_pos].next=0;
datas[used_pos].pre=tail->pre;
tail->pre->next=&datas[used_pos];
tail->pre=&datas[used_pos];
tail=&datas[used_pos];
used_pos++,used_length++;
}
T erase(int pos){
if(!is_pos_ok(pos)) assert("The wrong place is :"=="in erase function,the argument \"pos\" is not ok.");
list_node<T> *findpos=find(pos);
findpos->pre->next=findpos->next;
findpos->next->pre=findpos->pre;
used_length--;
}
};
#endif
这里有一个BUG,你们能发现吗?(关于指针的)
在评论区中回复这个BUG但没有回复解决方法的,测试通过(系统:Windows,IDE:Dev-C++,编译器:g++)后会有奖励:如果你写了文章(原创/转载/翻译均可),那么我会在几篇文章(最多3篇,如果文章数量不够3篇,那么我会全点赞)中点赞。(截止时间:2018年12月31日23:59)
在评论区中回复这个BUG和解决方法的,测试通过(系统:Windows,IDE:Dev-C++,编译器:g++)后会有奖励:如果你写了文章(原创/转载/翻译均可),那么我会在几篇文章(最多3篇,如果文章数量不够3篇,那么我会全点赞)中点赞。(截止时间:2018年12月31日23:59)
除了绿色+下划线部分以外的部分均可转载。转载时请附上本文链接:https://blog.csdn.net/weixin_41461277/article/details/84872583 。