1. 程式人生 > >C++資料結構-單鏈表建立

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;
}

編譯執行

總結
    - 連結串列中的資料元素在實體記憶體中無相鄰關係
    - 連結串列中的結點都包含資料域指標域
    - 頭結點用於輔助資料元素的定位,方便插入和刪除操作
    - 插入和刪除操作需要保證連結串列的完整性