C++数据结构之单链表

C++小虾米的第一篇文章–数据结构单链表

1.线性表

 线性表有两种结构,顺序存储结构和顺序存储结构,顺序存储结构的线性表理解起来很简单。
 对于顺序存储,我们首先能想到的数组,而顺序存储的线性表就是将结构体+数组的这一种组合来实现。例如:

#define MAXSIZE 20
	typedef int ElemType;
	typedef struct
{
	ElemType data[MAXSIZE];
	int length;
}sqList;

 使用的时候只需要创建一个sqList变量即可,顺序存储结构方便我们进行查找和修改操作,只需要知道它的下标就可以定位到指定元素。然而,对于顺序存储,如果我们需要插入或者删除元素,效率将是极低的。 例如,如果需要增加一个元素,为了不破坏顺序连接性,选好插入位置之后,必须将该位置之后所有的元素都向后移动一位,这就增加了时间复杂度,具体不谈。
 线性表还有一种结构:链式存储结构。链式存储结构结合了结构体和指针,使得元素和元素之间通过指针变量进行联系,你不一定要在我旁边,我知道你在哪就行。

	typedef struct Node
{
	ElemType data;					//数据域
	Struct Node* Next;				//指针域
}Node;

 链表有一个特殊的东西,叫做指针,这个指针指向了下一个元素,所以遍历的时候,我们只需要知道起始指针便可一步步往下遍历,找到最后一元素。约定了两个指针:head和tail,其中tail的next指针指向NULL。

2.新的操作

 原始的增删查改的操作在链表中行不通,我们重点要关注指针这个东西,指针和地址紧密联系,所以链表的增删查改操作要和指针联系起来,总结起来无非几句话:
1.插入操作:遍历到插入位置,前指针的next指向要插入的元素,插入元素的next指向前指针的next;
2.删除操作:遍历到删除位置,前指针的next指向删除位置的next,注意一下,删除的元素要free一下,释放空间!!!
3.查找操作:不多说,遍历比较;
4.修改操作:来个计数器,遍历到指定位置,修改数据域。

3.代码的分块实现

3.1 头文件.h

 本文用的简单的C++类写的一个链表,将插入删除等一系列方法放到Clinklist这个类中。
 头文件代码如下:

//文件名为Clinklist.h。
#pragma once
#include <stdlib.h>
#include <string.h>
#include <iostream>

typedef struct node
{
	char data;												
	//数据域
	struct node *ptr;										
	//指针域
}Node;

class Clinklist
{
public:
	Clinklist(void);										
	//构造函数:初始化变量
	virtual ~Clinklist(void);								
	//虚析构函数:本文用作free掉malloc的内存
private:
	Node *phead,*ptail;										
	//定义头指针和尾指针

public:
	//void sethead( Node *HEAD ){ phead = HEAD; };
	//Node* gethead(){ return phead;};	
	void menu();										
	//做了一个菜单
	void pushfront();									
	//初始化链表,向前插入数据
	void pushback();									
	//初始化链表,向后插入数据
	void deletenode(void);								
	//删除操作
	void insert(int loc,int data);						
	//插入操作:形参为位置和数据
	int getlength();									
	//获取链表长度
	void print();										
	//打印操作
	void freem();										
	//free内存
};

3.2函数实现.cpp

 接下来在cpp文件中实现头文件中的函数:

#include "Clinklist.h"

Clinklist::Clinklist(void)					
//构造函数
{
	phead = NULL;
	ptail = NULL;
}
Clinklist::~Clinklist(void)					
//析构函数
{
	freem();
}
void Clinklist::pushback()					
//初始化,向后插入操作
{
	char c;
	std::cin >>c;
	if( !phead )							
	//当phead = NULL时,申请空间给头指针,头指针此时等于尾指针,将尾指针下一指向置为NULL。
	{
		phead = (Node*)malloc(sizeof(Node));
		phead -> data = c;
		ptail = phead;
		ptail -> ptr = NULL;
	}
	else 
	{
		Node* p= (Node*)malloc(sizeof(Node));
		p -> data = c;
		ptail -> ptr = p;
		ptail = p;
		ptail -> ptr = NULL;
	}
}
void Clinklist::pushfront()				
//初始化,向前插入操作
{
	char c;
	std::cin >> c;
	if( !phead )
	{
		phead = (Node*)malloc(sizeof(Node));
		phead -> data = c;
		ptail = phead;
		ptail -> ptr = NULL;
	}
	//这里刚学的时候有点疑问,对phead赋值的话会不会改变原先的值?改变了地址,其实地址指向的东西没有变化。
	else
	{
		Node *p= (Node*)malloc(sizeof(Node));
		p -> data = c;
		p -> ptr = phead;
		phead = p;
	}
}
void Clinklist::freem()				
//释放空间很好理解,只要增加一个中间变量,从head开始,将下一个赋值给这个中间变量,同时释放当下变量,直到遍历到NULL停止。
{
	while (phead)
	{   
		Node *p = phead;
		phead = phead -> ptr;
		free(p);
	}
}
void Clinklist::print()					  			
//打印不必多说,遍历
{
	Node *p = phead;		
	while( p != NULL)
	{
		std::cout << p->data << std::endl;
		p = p -> ptr;
	}
}
void Clinklist::deletenode(void)				
//删除操作需要特别注意删除元素的位置
{
	Node *p = phead,*p2 = phead;
	std::cout <<"输入需要删除的节点数据"<<std::endl;
	char c ;
	std::cin >> c;
	while( p )
	{
		if( p->data == c)					 	
		//如果找到了这个元素,停止while
		{
			break;
		}
		p2 = p;									
		//p2指向这个节点的前一个节点
		p = p -> ptr;							
		//p为当前节点
	}
	if( p == NULL ) 						    
	//删除失败提醒
		std::cout << "没有那一个节点"<<std::endl;
	else 
	{											
	//需要注意这里的大括号,一直延伸到
		if( p == phead ) 
	{
		phead = p->ptr;
		free(p);
	}
	else
	{
		p2 -> ptr = p-> ptr;
		free(p);
	}	
	}											
}
int  Clinklist::getlength()						
//遍历,设置个计数器,返回一下
{
	Node *p = phead;
	int i = 0;
	while( p )
	{
		++i;
		p = p -> ptr;
	}
	return i;			
}
void Clinklist::insert(int loc,int data)				
//插入操作也需要注意一下插入的位置,本文做的比较不严谨,没有做前插后插
{
	Node *p = phead;
	int i = 1;
	int len = getlength();
	Node *p2 = (Node *)malloc(sizeof(Node));
	p -> data = data;
	if ( loc == 0)										
	//如果loc为0,则将数据插入到头指针
	{
		p2 -> ptr = phead;
		phead = p2;
	}
	else if (loc == len)								
	//如果loc等于链表长度,则将其插入尾指针
	{
		ptail-> ptr = p2;
		ptail = p2;
		ptail -> ptr = NULL;
	}
	else												
	//其他情况做个计数器,遍历到指定位置新建节点,连接前后就好
	{
		while( p )
		{
			p = p -> ptr;
			i++;
			if (loc == i)
			{ 
				p2 -> ptr = p -> ptr;
				p -> ptr = p2;
				break;
			}
		}}
}

void Clinklist::menu()									
//做了个小小的菜单
{
	std::cout<<"          Main Menu\n";
	std::cout<<"----------------------------------------------\n";
	std::cout<<"  (1) ------初始化:往前插入数据\n";
	std::cout<<"  (2) ------初始化:往后插入数据\n";
	std::cout<<"  (3) ------插入操作\n";
	std::cout<<"  (4) ------在表中删除数据\n";
	std::cout<<"  (5) ------获取表长\n";
	std::cout<<"  (6) ------打印\n";
	std::cout<<"  (7)------exit system\n";
	std::cout<<"----------------------------------------------\n";
}

3.3主函数.cpp

 接下来就是主函数了:

#include "Clinklist.h"

int main()
{
	Clinklist list;
	list.sethead(NULL);
	int loc,data;
	while(1)											//做一个循环
	{
		list.menu();
		printf("输入命令:\n");
		int ic ;
		std::cin >> ic;
		switch(ic)										//再来一个开关
		{
		case 1:
			{
				printf("Please input your info: ");
				list.pushfront();
				break;
			}
		case 2:
			{
				printf("Please input your info: ");
				list.pushback();
				break;
			}
		case 3:
			{
				std::cout <<"输入位置和需要插入的数据"<<std::endl;
				std::cin >> loc >> data;
				list.insert(loc,data);
				break;
			}
		case 4:
			{
				list.deletenode();
				break;
			}
		case 5:
			{
				printf("the length of list is %d\n",list.getlength());
				break;
			}
		case 6:
			{
				list.print();
				break;
			}
		case 7:
			{
				//list.freeMemory();
				exit(0);
			}

		}
	}
	return 0;
}

4.总结

作为数据结构的入门,链表发挥了它及其重要的作用,本文只介绍了单链表,对于链表的学习也是刚刚起步,关于多链表和循环链表,相信也只是在指针域上做做手脚,分析一下也并没有很难,只是要理解一下这种思想,链表在数据结构中处处体现,栈与队列,树等也将会使用到链表的知识,希望多多交流,希望文章有所帮助。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值