数据结构学习笔记(一) 链表及其基本操作

学习要点:

1 零初始化 

d = T()  如果T是基本数据类型 则表示0 

如果T是自定义数据类型 则使用无参构造函数

2 内部类型成员:

定义一个类型,受到访问权限影响,一般外部不能访问

3 内部成员指向动态内存时 需要重写析构 赋值 拷贝函数

4 指针的引用 Node*&p 表示指针本身 可以用来改变指针的指向.

一般在返回值和形参中出现这个


02list.h

#ifndef  LIST_H
#define LIST_H
#include <iostream>
using namespace std;

typedef int T;
class List
{
	//内部类型成员  受访问权限影响  
	struct Node
	{
		T data;
		Node* next;
		//一旦你写了构造函数 就不能再以前的方法初始化
		// d = T() 零初始化 基本数据类型表示0 自定义数据类型表示无参构造函数
		Node(const T& d = T() ):data(d),next(NULL) 
		{	
		}
	};
	
	Node* head; //头指针,用来保存头节点地址 。 有头节点的链表这里直接是Node对象
	int len;
public:
	List():head(NULL),len(0) { }
	
// 	//由于有指针成员指向动态内存时 需要重写三大函数 析构 赋值 拷贝构造
	~List();
	
	//拷贝赋值函数
	List(const List &l);
	
	//赋值函数
	List& operator=(const List& l);
	
	//函数的返回值如果是List型的 一定要是指针或者引用
	// 当没有重写析构 赋值 拷贝构造函数, 如果以值类型返回,由于head都指向同一个地方 ,在临时变量析构时会释放链表中所有的空间
	//
	//个数 有n个数则返回n
	int size() const;
	
	//前插
	void push_front(const T& d);
	//后插
	void push_back(const T&d);
	
	//找链表中指向指定位置的指针
	Node*& getptr(int pos);
	
	//在任意位置插入
	void insert(const T& d,int pos); //n个节点 有n+1个插入 pos  0~n

	//遍历
	void travel() const;
	
	//按位置删除
	void erase(int pos);
	
	//按数值来得到指向节点的指针
	int  find(T d);
	
	//按数值来删除节点 删除链表中所有的这个值
	void remove(T d);
	
	//修改节点
	void set(int pos ,T d);
	
	//判断是否为空
	bool empty() const;
	
	//返回头节点数据
	T front() const ;
	
	//返回尾节点数据
	T back() const;
	
	//链表反转
	//思路1 指针反转 3个临时指针
	void reverse();
	
	//链表反转的另一种思路
	//重新构造一个链表 原来的块头插进来 2个临时指针
	void reverse2();
	
	//从小到大排序
	void sort();
	
	//合并
	void add(List& l);
	
	//清空
	void clear();
};

#endif


02list.cpp

#include "02list.h"
#include <iostream>
using namespace std;


// 	//由于有指针成员指向动态内存时 需要重写三大函数 析构 赋值 拷贝构造
List::	~List()
	{
		clear();
	}
	
	//拷贝赋值函数
List::	List(const List &l)
	{
		head = NULL;
		len = 0;
		Node *p = l.head;
		while(p != NULL)
		{
			push_back(p->data);
			p = p->next;
		}
	}
	
	//赋值函数
	List& List::operator=(const List& l)
	{
		clear();
		Node *p = l.head;
		while(p != NULL)
		{
			push_back(p->data);
			p = p->next;
		}
		
		return *this; 
	}
	
	
	
	
	//函数的返回值如果是List型的 一定要是指针或者引用
	// 当没有重写析构 赋值 拷贝构造函数, 如果以值类型返回,由于head都指向同一个地方 ,在临时变量析构时会释放链表中所有的空间
	//
	//个数 有n个数则返回n
	int List::	size() const
	{
		return len;
	}
	
	
	//前插
	void List::	 push_front(const T& d)
	{
		/*
		Node* p = new Node(d); //这里不可以用局部变量 因为局部变量会销毁
		p->next = head;
		head = p;
		*/
		insert(d,0);
	}
	//后插
	void List::	 push_back(const T&d)
	{
		insert(d,size()); //少用一次变量  出错几率就少一次 len 与 size()的区别
	}
	
	//找链表中指向指定位置的指针
	// 返回引用类型  保证返回的是指针本身
	// 如果不是引用 那就是值传递了。
	//pos 从0~n 有n+1个位置  0~n都可以插值  位置0~n-1是用来删除和修改的 
	 //因为我想在将来改变返回的这个指针的指向,所以写引用。
	 //若是值传递,我在调用函数后只能得到一个相同指向的指针,这样只能修改指向的对象的值
	 //,但却无法修改返回的这个指针的指向。
	List::Node*&  List::	 getptr(int pos)  //	List::Node 内部类型
	{
		if(pos == 0) return head;
		
		Node *p = head;
		//1 return p->next
		//2 p=p->next ; return p->next
		for(int i =1; i < pos; i++)
		{
			p = p->next;
		}
		return p->next;
	}
	
	//在任意位置插入
	//1在链表里找到指向这个位置的指针
	//2 新节点指和找到的指针指向同一个地方
	//3 指针指向新节点 ++len
	void  List:: insert(const T& d,int pos) //n个节点 有n+1个插入 pos  0~n
	{
		if(pos <0 || pos > size() ) pos = 0;
		Node* &p = getptr(pos); //继续引用 
		Node *pn  = new Node(d);
		pn -> next = p;
		p = pn;
		len ++;
	}
	
	//遍历
	void List::	travel() const
	{
		Node *p = head;
		while(p != NULL)
		{
			cout << p->data  << ' ';
			p = p->next;
			
		}
		cout << endl;
	}
	
	//按位置删除
	//插入可以有n+1个位置 删除有n个位置 0~n-1
	//1找到指向这个节点的指针
	//2 保存指针 为temp  指针指向这个节点的下一个节点 
	//3 释放temp的空间  --len
	void List::	 erase(int pos)
	{
		if(pos < 0 || pos > size() -1) //无效位置
		{
			return;
		}
		
		
		Node* &p = getptr(pos);
		Node *temp = p;
		p = p->next;
		delete temp;
		--len;
		
	}
	
	//按数值来得到指向节点的指针
	int List::	find(T d)
	{
		Node *p = head;
		int pos = 0;
		while( p != NULL)
		{
			if( (p->data) == d ) return pos;
			p = p->next;
			pos ++;
		}
		return -1;
	}
	
	
	//按数值来删除节点 删除链表中所有的这个值
	void List::	remove(T d)
	{
		int pos;
		while( (pos=find(d)) != -1 )
		{
			erase(pos);
		}
	}
	
	//修改节点
	void List::	set(int pos ,T d)
	{
		if(pos < 0 || pos > size()-1) return;
		getptr(pos) -> data = d;
	}
	
	bool List::empty() const
	{
		return head==NULL;
	}
	
	//返回头节点数据
	T List::front() const 
	{
		if(empty() ) throw "链表为空";
		return head->data;
	}
	
	//返回尾节点数据
	T List::back() const
	{
		if( empty() ) throw "链表为空";
		Node *p = head;
		while(p->next != NULL)
		{
			p = p->next;
		}
		
		return p->data;
	}
	
	//链表反转
	//思路1 指针反转 3个临时指针
	void List::	reverse()
	{
		//因为前面用了指针的引用,让我出现疑惑 
		//这里为什么不用引用呢?
		//因为无论是函数形参还是返回值都存在值传递的问题,导致实际操作的
			//是一个相同指向的临时指针 。
		//而这个函数中不存在传递问题,是对指针的直接操作。
		
		Node *p = head;
		Node *p_next = NULL;
		Node *p_pre =NULL;
		
		while(p != NULL)
		{
			p_next = p->next;
			p->next = p_pre;
			p_pre = p;
			p = p_next;
		}
		head = p_pre;
	}
	
	//链表反转的另一种思路
	//重新构造一个链表 原来的块头插进来 2个临时指针
	void List::	reverse2()
	{
		Node*p = head;
		Node *q;
		head = NULL;
		while(p != NULL)
		{
			q = p->next;
			p->next = head;
			head = p;
			p = q;
		}
	}
	
	//从小到大排序
	void List::	sort()
	{
		Node *p = head;
		Node *q;
		int temp;
		while( (q=p->next) !=NULL)
		{
			if(p->data > q->data)
			{
				temp = p->data;
				p->data = q->data;
				q->data = temp;
				p = head;
			}
			else
			{
				p = q;
			}
		}
	}
	
	//合并
	void List::	add(List& l)
	{
		//  getptr(size())  = l.head; 这样子是不安全的 对l的操作会影响本链表
		Node *p = l.head;
		while(p!=NULL)
		{
			push_back(p->data);
			p=p->next;
		}
	}
	
	
	//清空
	void List::	clear()
	{
		while(head != NULL)
		{
			Node* p  = head->next;
			delete head;
			head = p;
		}
		head = NULL;
		len = 0;
	}


main.cpp

#include <iostream>
#include "02list.h"
using namespace std;

int main()
{
	List l;
	cout << sizeof(l) << endl; // 大小 8 
	
	l.push_front(1);//头插
	l.push_front(10);
	l.push_front(100);
	l.travel(); //100 10 1 

	l.insert(1000,0); 
	l.travel();//1000 100 10 1
	l.push_back(10000); //1000 100 10 1 10000 
	l.travel();
	
	l.insert(70,-1); // pos不正确 纠正为0
	l.travel();//70 1000 100 10 1 10000
	l.insert(7000,200); //pos不正确 纠正为0
	l.travel();//7000 70 1000 100 10 1 10000 
	
	l.erase(100); //无效的位置  直接返回
	l.travel();//7000 70 1000 100 10 1 10000 
	l.erase(5);//删除5号位置 即第六个位置
	l.travel(); //7000 70 1000 100 10 10000
	
	
	//插入重复的值
	l.insert(999,2);
	l.insert(999,5);
	l.insert(999,6);
	l.travel();//7000 70 999 1000 100 999 999 10 10000 
	l.remove(999); //按值删除
	l.travel();//7000 70 1000 100 10 10000
	
	//修改
	l.set( l.find(10) , 9 ); //将10改成9
	l.travel();//7000 70 1000 100 9 10000
	l.set(0,8);//将第0个改成8
	l.travel();//8 70 1000 100 9 10000
	
	
	cout  << boolalpha  << l.empty() << endl; //false	
	cout << l.front() << ' ' << l.back() << endl;//8 10000
	
	//cout << l.getptr(l.size() -1)->data << endl;
	cout << "reverse : ";
	l.reverse();
	l.travel();
	l.reverse2();
	cout << "reverse2: ";
	l.travel();
	
	l.sort();
	cout << "sort :";
	l.travel();
	l.reverse();
	cout << "reverse";
	l.travel();	

	//拷贝构造函数
	
	List l2(l);
	cout << "l2 travel:";
	l2.travel();
	
	List l3;
	l3.push_back(1);
	l3.push_back(2);
	l3.push_back(3);
	cout << "l3 travel :" <<endl;
	l3.travel();
	l2 = l3;
	cout << "l2 travel :" <<endl;
	l2.travel();
	
	l2.add(l3);
	cout << "l2 travel :" <<endl;
	l2.travel();
	cout  << "size " << l2.size() << endl;
	
		
	//清空
	l3.clear();
	while( !l.empty() )
	{
		l.erase(0);
	}
	cout << "size :"  << l.size() << endl;
	return 0;
	
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值