面向对象程序设计——单链表

概念:

在这里插入图片描述

正如这幅图中所表示的那样,单链表就是由可能不连续的数据所组合而成的数据结构。 其中每个数据分为两部分,一部分是数据存储的位置,称为数据域,另外指针所存储的地方,称为指针域

单链表节点定义

struct

struct node
{
    int data;               //存储链表数据
    node *next;             //存储结点的地址
    node(int e=0,node *nextval=nullpte):data(e),next(nextval){}//构造函数
};

class

class node
{
    int data;
    node *next;
public:
    node(int dval=0,node *nval=nullptr){
        data=dval;
        next=nval;
    }
    int getdata(){
        return data;
    }
    node* getnext(){
        return next;
    }
    void setdata(int dval){
        data=dval;
    }
    void setnext(node* nval){
        next=nval;
    }
};

单链表类定义

成员函数

为带头结点单链表的成员函数

class list
{
    private:
    	node *head;               //头结点
    public:
    	list();                   //构建一个单链表
    	~list();                  //销毁一个单链表
        void creatlist(int num[],int n);//创建一个单链表
    	void inset(int pos,int value);  //在第pos个节点后插入新节点,数据为value,如插入失败输出“error”
    	void print();             //以head为表头依次输出每个节点数据值
    	void remove(int pos);     //删除第pos个节点,若不成功输出error
    	int getlength();          //获取单链表长度
    	node* find(int i);        //查找第i个节点
};

单链表创建(带头结点)

表尾插入需要对head分空和非空的判断。同理,若插入、删除第一个结点,需要修改头指针head, 其它位置不需要,造成编程的麻烦。

为方便实现,增加头结点,即:head = new cnode(0,nullptr); 该结点不存数据。第一个数据在head->next。

头结点优点:head值始终不变。不需在程序中加入if…else。

头插

在这里插入图片描述


list::list(){
	head=new node;                      //为头结点开辟内存空间
}
void list::creatlist(int num[],int n){
	for(int i=0;i<n;i++){
		node *s=new node(num[i],head->next);   //将头指针所指向的下一个结点的地址,赋给新创建结点的next
		head->next=s;                         //将新创建的结点的地址赋给头指针的下一个结点
	}
}
输入:5 
     1 2 3 4 5
输出:5 4 3 2 1

头插法创建链表的根本在于深刻理解最后两条语句

s->next = head->next; // 将头指针所指向的下一个结点的地址,赋给新创建结点的next
head->next = s; // 将新创建的结点的地址赋给头指针的下一个结点

尾插

在这里插入图片描述


list::list(){
	head=new node;                      //为头指针开辟内存空间
}
void list::creatlist(int num[],int n){
	node *tail=head;                    //定义尾结点,未创建其余结点之前,只有一个头结点
	for(int i=0;i<n;i++){
		node *s=new node;               //为新结点开辟新内存
		tail->next=s;                   //将新开辟的node的地址赋给head的下一个结点地址。
		s->data=num[i],s->next=nullptr; //新结点的数据域赋值
		tail=s;                         //尾地址变为新节点
	}             
}
输入:5 
     1 2 3 4 5
输出:1 2 3 4 5

尾插法深刻理解:

tail->next = s;

tail = s;

①:第一个结点,tailhead 共用一块内存空间。现在从堆中心开辟出一块内存给 s,将 s 的数据域赋值后,此时 tail 中存储的地址是 head 的地址。此时,tail->next 代表的是头结点的指针域,因此 tail->next = s 代表的就是将上一个,也就是新开辟的 s 的地址赋给 head 的下一个结点地址。

②此时,tail->next 的地址是新创建的 s 的地址,而此时 tail 的地址还是 head 的地址。 因此 tail = s ,这条作用就是将新建的结点 s 的地址赋给尾结点 tail。 此时 tail 的地址不再是头结点,而是新建的结点 s

总结

由上面的例子以及比较,我们可以看见:

  1. 头插法相对简便,但插入的数据与插入的顺序相反;
  2. 尾插法操作相对复杂,但插入的数据与插入顺序相同。

单链表创建(不带头结点)

(结点为类定义)
public:
	list() :head(nullptr) {} //直接置为空
	void createlistinhead(int num[], int n)
	{
		for (int i = 0; i < n; i++)
		{
			// new s
			node* s = new node(num[i], head);
			head = s;
		}
	}
	void createlistintail(int num[], int n)
	{
		node* tail = nullptr;
		for (int i = 0; i < n; i++)
		{
			// new s
			node* s = new node(num[i]);
			if (head == nullptr)        //不带头结点加if else判断
				head = s;
			else
				tail->setnext(s);
			tail = s;
		}
	}

链表输出

带头结点


void list::print(){
	node *p=head->next;   //注意从head的下个结点开始,head不存数据
	while(p!=nullptr){
		cout<<p->data;
		p=p->next;
	}
	cout<<endl;
}

不带头结点


void list::print(){
	node *p=head;   //注意从head开始,head存数据
	while(p!=nullptr){
		cout<<p->data;
		p=p->next;
	}
	cout<<endl;
}

插入结点


void list::inset(int pos,int value){
	if(pos<1||pos>getlength()) cout<<"error\n";
	else{
		node *temp=new node;
		temp->data=value;
		node *p=head->next;    //如果不带头结点 node *p=head;
		int i=1;
		while(pos>i){
			p=p->next;
			i++;
		}
		temp->next=p->next;
		p->next=temp;
	}
}

删除结点


void list::remove(int pos){
	if(pos<1||pos>getlength()) cout<<"error\n";
	else{
		if(pos==1){  //对头结点进行特判
			node *temp=head->next;   //创建占位结点指向头结点的下一个
			delete head;			 //删除头结点
			head=temp;
		}else{
			node *a=head,*b=head->next;  
			int i=1;
			while(i<pos){            //a 循环到目标节点的前一个结点
				a=b;                 //b 循环到目标节点
				b=b->next;
				i++;
			}
			a->next=b->next;         //删除目标节点
			delete b;
			
		}
	}
}

获取单链表长度


int list::getlength(){
	int cnt=0;
	node *p=head->next;   //注意
	while(p!=nullptr){
		cnt++;
		p=p->next;
	}
	return cnt;
}

查找结点


node*list::find(int i){
	if(i<0||i>getlength())
		return nullptr;
	node *p=head;
	for(int k=0;k<i&&p;k++)
		p=p->next;
	return p;
} 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值