[朝花夕拾]單鏈表篇-C++
本例結合模板對單鏈表進行了一個簡單的C++實現,可爲數據結構新手進行學習提供參考,中間可能有考慮不周之處煩請指出,該文章屬於作者原創文章,當前時間節點前全網沒有第二篇,轉載請註明出處,支持個人勞動成果。
單鏈表結構定義-
頭文件 singly_list.h
#ifndef SIGNLY_LIST
#define SIGNLY_LIST
#include<iostream>
using namespace std;
template<typename T>
class Node {
public:
Node(const T& _data, Node* _next = NULL)
{
data = _data; // 數據結構T若不對=進行重載就會發生淺拷貝現象,導致內存錯誤
next = _next;
}
public:
T data;
Node* next;
};
template<typename T>
class SinglyList {
private:
Node<T>* header; // 頭節點指針
int m_size; // 鏈表長度
private:
bool isInvalidPos(int pos);
public:
SinglyList();
~SinglyList();
bool isEmpty()const;
int size();
void add(const Node<T>& _node,int pos);
void addToTail(const Node<T>& _node);
void remove(int pos);
void update(const Node<T>& _node,int pos);
Node<T> getNode(int pos);
void printList();
};
#endif
template<typename T>
inline bool SinglyList<T>::isInvalidPos(int pos)
{
return (pos<0 || pos>=m_size)?true:false;
}
// 初始化單鏈表
template<typename T>
inline SinglyList<T>::SinglyList()
{
header = NULL;
m_size = 0;
}
// 析構函數,釋放單鏈表空間
template<typename T>
inline SinglyList<T>::~SinglyList()
{
if (m_size > 0) {
// destroy memory
Node<T>* ptr = header;
while (header->next != NULL) {
header = header->next;
ptr->next = NULL;
delete ptr;
ptr = header;
}
}
}
// 判斷單鏈表是否爲空或長度是否爲零
template<typename T>
inline bool SinglyList<T>::isEmpty() const
{
return m_size==0 || header==NULL;
}
// 返回單鏈表長度
template<typename T>
inline int SinglyList<T>::size()
{
return m_size;
}
// 在單鏈表某個位置增加一個節點
template<typename T>
inline void SinglyList<T>::add(const Node<T>& _node, int pos)
{
if (isInvalidPos(pos)) {
cout << "pos invalid!\n";
return;
}
else {
Node<T>* prev = header;
Node<T>* ptr=header;
for (int i = 0; i < pos; i++) {
ptr=ptr->next;
if (i == pos - 1)
continue;
prev=prev->next;// 保證在pos的前一個節點
}
// 當前ptr指針指向位置爲pos處的節點
// 設置新節點的next指向該pos處的節點的起始位置
Node<T>* node = new Node<T>(_node.data,ptr);
prev->next = node;// prev指針指向剛纔新添加入的節點的起始地址
m_size++;
}
}
// 在單鏈表的末尾增加一個節點
template<typename T>
inline void SinglyList<T>::addToTail(const Node<T>& _node)
{
if (m_size == 0) {
header = new Node<T>(_node.data, NULL);
m_size++;
}
else if(m_size>0){
Node<T>* prev = header;
Node<T>* ptr = header;
while (ptr) {
ptr=ptr->next;
if (ptr == NULL) {
cout << "end while.\n";
break;
}
prev = ptr;
}
if (prev != NULL) {
prev->next = new Node<T>(_node.data, NULL);
}
m_size++;
}
}
// 移除單鏈表中某個位置的節點
template<typename T>
inline void SinglyList<T>::remove(int pos)
{
if (isInvalidPos(pos)) {
cout << " invalid pos! \n";
return;
}
else {
if (pos > 0) {
Node<T>* prev = header;
Node<T>* ptr = header;
for (int i = 0; i < pos; i++) {
ptr = ptr->next;
if (i == pos - 1)
continue;
prev = prev->next;
}
prev->next = ptr->next;
ptr->next = NULL;
delete ptr; // 釋放掉這塊內存,不知道是否存在問題,如果這塊內存不是從堆區申請的,那咋辦嘞
ptr = NULL;
}
else if(pos==0){
Node<T>* ptr = header;
header = header->next;
ptr->next = NULL;
delete ptr;
ptr = NULL;
}
}
}
// 對單鏈表中某個位置的節點進行更新
template<typename T>
inline void SinglyList<T>::update(const Node<T>& node, int pos)
{
if (isInvalidPos(pos)) {
cout << "invalid pos!\n";
return;
}
else {
Node<T>* ptr = header;
for (int i = 0; i < pos; i++) {
ptr=ptr->next;
}
ptr->data = node.data;
}
}
// 得到單鏈表中某個位置的節點數據
template<typename T>
inline Node<T> SinglyList<T>::getNode(int pos)
{
if (isInvalidPos(pos)) {
cout << "invalid pos!\n";
return Node<T>(T());
}
else {
Node<T>* ptr = header;
for (int i = 0; i < pos; i++) {
ptr=ptr->next;
}
Node<T> node = Node<T>(ptr->data, ptr->next);
return node;
}
}
template<typename T>
inline void SinglyList<T>::printList()
{
if (m_size <= 0) {
cout << "There is no data in list...\n";
return;
}
else {
Node<T>* ptr = header;
do {
// 要求T數據進行<<流操作符的重載,所以說這個函數定義在SinglyList應該是不妥當的,暫且先這樣處理
cout << ptr->data;
ptr = ptr->next;
} while (ptr!=NULL);
}
}
測試數據結構定義
頭文件 student.h
#pragma once
#include<string>
#include<iostream>
using namespace std;
class Student {
public:
Student();
Student(long id, string name);
~Student();
friend ostream& operator<<(ostream& os,const Student& _student);
private:
long m_id=-1L;
string m_name="invalid name";
};
源文件 student.cpp
#include "student.h"
Student::Student()
{
}
Student::Student(long id, string name)
{
m_id = id;
m_name = name;
}
Student::~Student()
{
}
ostream& operator<<(ostream& os, const Student& _student)
{
// TODO: 在此處插入 return 語句
return (os<<"[ " << _student.m_id << ", "<<_student.m_name<<" ]\n");
}
簡單的測試函數編寫-main函數
#include "singly_list.h"
#include "student.h"
#include<iostream>
using namespace std;
int main() {
SinglyList<Student> list;
// 測試addToTail函數
list.addToTail(Student(1L, "鳴人"));
list.addToTail(Student(2L,"佐助"));
list.addToTail(Student(3L, "旗木卡卡西"));
// 測試size函數
cout << "當前單鏈表的長度爲: " << list.size() << "\n";
// 輸出當前鏈表信息
list.printList();
cout << "\n";
// 測試add函數
list.add(Student(4L, "斑"),2);
// 輸出當前鏈表信息
cout << "當前單鏈表的長度爲: " << list.size() << "\n";
list.printList();
// 待測函數
cout << "測試 isEmpty函數...\n";
cout << (list.isEmpty() ? "是" : "否") << "\n";
cout << "測試remove函數...\n";
list.remove(0);
list.printList();
list.addToTail(Student(7L, "紅凱"));
list.printList();
// 測試update函數
cout << "測試update函數...\n";
Node<Student> node(Student(101L,"我愛羅"));
list.update(node, 3);
list.printList();
// 越界測試
cout << "測試update函數(越界測試)...\n";
list.update(node, 10);
list.printList();
cout << "測試getNode函數(正常位置)...\n";
Node<Student> stu1=list.getNode(2);
cout << stu1.data;
cout << "測試getNode函數(越界測試)...\n";
Node<Student> stu2 = list.getNode(10);
cout << stu2.data;
return 0;
}