C++数据结构-单链表创建

    前边我们创建了顺序存储结构的线性表,简称顺序表,顺序表最大的问题是:插入和删除需要移动大量的元素。为了解决 这个问题, 我们引入链式存储结构的线性表,简称链表,链表与顺序表不同,链表的每个结点在内存中是分开存放的,每个结点都包含数据域和指针域:
    - 数据域存储数据元素本身
    - 指针域存储相邻结点的地址

链式存储结构的线性表有
    - 单链表每个结点只包含直接后继的地址信息
    - 循环链表单链表中的最后一个结点的直接后继为第一个结点
    - 双向链表单链表中的结点包含直接前驱和后继的地址信息
    - 双向循环链表双向链表的最后一个结点的后继为第一个结点,第一个结点的前驱为最后一个结点

    链表中的基本概念
    - 头结点 : 链表中的辅助结点,包含指向第一个数据元素的指针
    - 数据结点:链表中代表数据元素的结点,包含数据元素与地址两部分
    - 尾结点:链表中的最后一个数据结点,包含的地址信息为空

    这里我们创建的单链表,结点可以 定义为如下

struct Node : public MyObject
{
    T value;    //数据域
    Node* Next;    //指针域
};

    单链表的内部结构如下,头结点不存储实际的数据元素,只是为了数据元素定位,方便插入和删除操作

下边我们直接来看代码中的实现

#ifndef __LINKLIST_H__
#define __LINKLIST_H__

#include "List.h"

namespace MyLib
{
template <typename T>
class LinkList : public List<T>
{
private:
    struct Node : public MyObject
    {
    	T value;	//数据域
    	Node* Next;	//指针域
    };

    mutable struct : public MyObject
    {
	char reserved[sizeof(T)]; //作为占位用,不存放数据
	Node* Next;
    }m_header;	
    int m_length;	//存储链表长度

    Node* m_current;	//指向当前的结点
    int m_step;			//
	
    Node* position(int index) const		//获取index处的结点
    {
	Node* ret = reinterpret_cast<Node*>(&m_header);
	for (int i=0; i<index; i++)
        {
	    ret = ret->Next;
	}
	return ret;
    }
    virtual Node* create()
    {
	return new Node;
    }

    virtual void destroy(Node* p)
    {
	delete p;
    }

public:
    LinkList()
    {
	m_length = 0;
	m_header.Next = NULL;
	m_current = NULL;
	m_step = 1;
    }

    bool insert( const T& e)	//尾部插入结点
    {
	return insert(m_length, e);
    }

    bool insert(int index, const T& e)	 //插入结点
    {
	bool ret = ( (0 <= index)&&(index <= m_length) );
	if (ret)
	{
	    Node* currentNode = position(index);
	    Node* newNode = create();
	    newNode->value = e;
	    newNode->Next = currentNode->Next;
	    currentNode->Next = newNode;
	    m_length++; 
	}
	else
	{
	    THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Bounds Exception...");
	}

	return ret;
    }

    bool remove(int index)	//删除结点
    {
	bool ret = ( (0 <= index)&&(index < m_length) );
	if (ret)
	{
	    Node* currentNode = position(index);
	    Node* toRemove = currentNode->Next;
            if (m_current == toDel)
	    {
	        m_current = toDel->Next;
	    }
	    currentNode->Next = toRemove->Next;
            m_length--;
	    destroy(toRemove);
	}
	else
	{
	    THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Bounds Exception...");
	}
	return ret;
    }

    bool get(int index, T& e)	//获取具体位置的结点元素
    {
	bool ret = ( (0 <= index)&&(index < m_length) );
	if (ret)
	{
	    Node* currentNode = position(index);
            e = currentNode->Next->value;
	}
	else
	{
	    THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Bounds Exception...");
        }
	return ret;
    }
    bool set(int index, const T& e)const	//设置具体位置的结点元素
    {
	bool ret = ( (0 <= index)&&(index < m_length) );
	if (ret)
	{
	    Node* currentNode = position(index);
	    currentNode->Next->value = e;
	}
	else
	{
	    THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Bounds Exception...");
	}
	return ret;
    }

    int find(const T& e) const		//链表中寻找元素位置
    {
	Node* tofind = m_header.Next;
	for (int i=0; i<m_length; i++)
	{
	    if (e == tofind->value)
	    {
		return i;
	    }
	    else
	    {
		tofind = tofind->Next;
	    }
	}
	return -1;
    }

    void move(int index, int step = 1)	//游标移动初始化
    {
	bool ret = ( (0<=index)&&(index < m_length) );
	if (ret)
	{
	    m_current = position(index)->Next;
	    m_step = step;
	}
	else
	{
	    THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Exception...");
	}
    }

    void next()	//游标移动到下一个位置
    {
	int i=0;
	while ( (!end())&&(i<m_step) )
	{
	    m_current = m_current->Next;
	    i++;
	}
    }

    bool end()	//判断游标是否到达链表最尾处
    {
        return m_current == NULL;
    }

    T current()	//返回游标所指处的元素
    {
        if (!end())
	{
	    return m_current->value;
	}
	else
	{
	    THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Exception...");
	}
    }

    int length()const	//返回列表长度
    {
	return m_length;
    }
    void clear()	//清空列表
    {
        while(m_header.Next)
	{
            remove(m_length-1);
	}
    }
};
}

#endif	//__LINKLIST_H__

上边我们就基本把单链表的内容实现完了,下边我们来在main函数中使用一下

#include <iostream>
#include <string>
#include "LinkList.h"

using namespace std;
using namespace MyLib;

int main()
{
    LinkList<int> list;

    for (int i=0; i<10; i++)
    {
	list.insert(i);
    }

    list.set(2, 23);

    for (list.move(0); !list.end(); list.next())	//采用游标方式遍历链表
    {
	cout << list.current() << endl;
    }

    cout << "find(3) = " << list.find(3) << endl;

    system("pause");

    return 0;
}

编译执行

总结
    - 链表中的数据元素在物理内存中无相邻关系
    - 链表中的结点都包含数据域指针域
    - 头结点用于辅助数据元素的定位,方便插入和删除操作
    - 插入和删除操作需要保证链表的完整性

  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
单链表是一种常见的数据结构,用于存储一系列的节点连接起来的数据。 在C语言中,我们可以使用结构体来定义一个节点,包含一个数据域和一个指向下一个节点的指针域。代码如下: ```c struct Node { int data; struct Node* next; }; ``` 然后,我们可以定义一个指向链表头部的指针,表示整个链表的起始位置。初始化链表时,这个指针可以指向NULL。 ```c struct Node* head = NULL; ``` 接下来,我们可以实现一些基本的操作,例如插入节点、删除节点、遍历链表等。 插入节点的过程包括创建一个新节点,并将其插入到链表中适当的位置。 ```c void insertNode(int value) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->data = value; newNode->next = NULL; if (head == NULL) { head = newNode; } else { struct Node* curr = head; while (curr->next != NULL) { curr = curr->next; } curr->next = newNode; } } ``` 删除节点的过程需要找到目标节点,并调整前后节点的指针。 ```c void deleteNode(int value) { if (head == NULL) { return; } if (head->data == value) { struct Node* temp = head; head = head->next; free(temp); return; } struct Node* prev = head; struct Node* curr = head->next; while (curr != NULL && curr->data != value) { prev = curr; curr = curr->next; } if (curr != NULL) { prev->next = curr->next; free(curr); } } ``` 遍历链表的过程是从头节点开始,依次打印每个节点的数据。 ```c void printList() { struct Node* curr = head; while (curr != NULL) { printf("%d ", curr->data); curr = curr->next; } printf("\n"); } ``` 这是一个简单的单链表的实现示例,你可以根据需要进一步扩展和优化。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值