本人弱鸡,在学数据结构,学到链表这一章,思索实践搜集资料了好几天才对单链表有了一些理解。现在整理一些代码与感悟,希望看到这篇文章也同样在学链表的童鞋能有所感悟,少走些弯路。
结点类(我比较喜欢用结构体,这样可避免友元的问题,也比较清晰) :
template <class T>
struct chainNode {
T data;
chainNode<T> *link;
chainNode(chainNode <T> *pre = NULL) { link = pre; }
chainNode(const T& item, chainNode<T> *pre = NULL) {
data = item;
link = pre;
}
};注意上述结构体中的两个构造函数,first = new chainNode<T>; 调用的便是第一个构造函数,这个first指针指向的便是不带值的头结点(头结点也可以带值,但不算是我们期待的链表中的值)。
first = new chainNode<T>(x),这调用的是第二个构造函数,这时候链表便不带头结点。
List类:
template <class T>
class List {
public:
List() { first = new chainNode<T>; } //带头结点
List(const T& x) { first = new chainNode<T>(x); } //这是不带头结点的
List(List<T> &L); //复制构造函数
~List() { makeEmpty(); }
chainNode<T>* locate(int i); //返回链表中第i个元素的地址
void makeEmpty(); //将链表清空
bool IsEmpty() const { return first->link == NULL ? true : false; }
void inputFront(T endTag);
void inputRear(T endTag);
int length() const; //计算带有头结点的链表的长度
int search(T &x); //返回链表第一个x是第几个值
void find(int k, T& x); //找到链表中第k个元素,并把它的值赋给x
void output(ostream& out);
bool remove(int k, T& x);
bool insert(int k, T& x);
void reverse();
private:
chainNode<T> *first; // 链表头指针
};请看构造方法List(),就如原先说的一样,我建立的便是带头结点的链表。带头结点的目的是为了让链表的一些操作比如插入的形式一致,不必根据插入的不同情况而分情况讨论。
以下是各个方法的具体实现(有关键注释,就不一一赘述了):
template<class T>
chainNode<T>* List<T>::locate(int i)
{
if (i<0) { return NULL; }
chainNode<T>* current = first;
int k = 0;
while (current != NULL && k<i) {
current = current->link;
k++;
}
return current;
}
template<class T>
void List<T>::makeEmpty()
{
chainNode<T> *q;
while (first->link != NULL) //留了一个first结点
{
q = first->link;
first->link = q->link;
delete q;
}
}
template<class T>
void List<T>::inputFront(T endTag)
{
cout << "前插法生成链表 " << endl;
chainNode<T> *newNode;
T val;
makeEmpty();
cout << "请输入第一个结点的值:" << endl;
cin >> val;
while (val != endTag) {
newNode = new chainNode<T>(val); //创建新结点
if (newNode == NULL) { cerr << "存储分配错误!" << endl; exit(1); }
newNode->link = first->link;
first->link = newNode;
cout << "请输入值:";
cin >> val;
}
}
template<class T>
void List<T>::inputRear(T endTag)
{
chainNode<T> *p, *q;
T val;
makeEmpty();
cout << "请输入链表的第一个值" << endl;
cin >> val;
q = first;
while (val != endTag) {
p = new chainNode<T>(val);
if (p == NULL) {cerr << "存储分配错误" << endl;exit(1);}
q->link = p;
q = p;
cout << "请输入值: ";
cin >> val;
}
q->link = NULL;
}
template<class T>
int List<T>::length() const
{
chainNode<T> *p = first->link;
int count = 0;
while (p != NULL) {
p= p->link;
count++;
}
return count;
}
template<class T>
int List<T>::search(T & x)
{
chainNode<T> *p = first->link;
int i = 1;
while (p -> data != x )
{
p = p->link;
i++;
}
return i;
}
template<class T>
void List<T>::find(int k, T & x)
{
if (k < 0) { cout << "k不合法,无法find第k个元素"; return; }
chainNode<T> *p = first->link;
for (int i = 1; i < k; i++) {
p = p->link;
}
x = p->data;
}
template<class T>
void List<T>::output( ostream& out)
{
chainNode <T>* current = first->link ;
//for (current = first->link; current !=NULL; current = current->link) {
//out << current->data << " ";
//}
while (current !=NULL) {
cout << current->data << " ";
current = current->link;
}
}
template<class T>
bool List<T>::remove(int k, T & x)
{ //将链表中的第i个元素删去,通过引用型x返回该元素的值
chainNode<T> * current = locate(k - 1);
if (current == NULL || current->link == NULL) return false;
chainNode<T>* del = current->link; //这是那个被删除的结点
current->link = del->link;
x = del->data;
delete del;
return true;
}
template<class T>
bool List<T>::insert(int k, T & x)
{//将新元素x插入在链表中第k个结点之后
chainNode<T>* current = locate(k);
if (current == NULL) return false;
chainNode<T> * newNode = new chainNode<T>(x); //生成新结点
if (newNode == NULL) { cerr << "存储分配错误!" << endl; exit(1); }
newNode->link = current->link;
current->link = newNode;
return true;
}
template<class T>
void List<T>::reverse() //带有头节点的链表反序
{
chainNode<T> *p, *q, *t;
p = first->link;
q = first->link->link;
t = NULL;
while (q != NULL) {
t = q->link;
q->link = p;
p = q;
q = t;
}
first->link->link= NULL; //设置末结点
first->link = p; //改变first结点
}
//重载<<
template <class T>
ostream& operator<< (ostream& out, List<T>& x) {
x.output(out);
return out;
}注意以上的所有代码,我都是以头文件的形式存在的。
看看测试类:
#include "stdafx.h"
#include "chain.h"
using namespace std;
int main()
{
List<int> l;
cout <<"链表是否为空 "<< l.IsEmpty() << endl;
l.inputFront(5);
//cout << l.IsEmpty() << endl;
//cout << l << endl;
//l.makeEmpty();
//cout << l << "makeEmpty()" << endl;
//nt i = 8;
//int j;
/*l.insert(0, i);
cout << l << endl;
l.remove(1, j);
cout << l << endl;
cout << j << endl;
cout << l.locate(1) << endl;
cout << l.locate(2) << endl;
cout << l.locate(3) << endl;
cout << l.locate(4) << endl;
*/
cout << l << endl;
l.inputRear(5);
cout << "后插法生成的链表为:" << endl;
cout << l << endl;
cout << "链表的长度为: " << l.length() << endl;
int j = 2;
int i = l.search(j);
cout << "j = " << j << "是链表中第" << i << "个元素" << endl;
l.find(j, i);
cout << "链表中第" << j << "个值为 " <<i << endl;
l.reverse();
cout << l << endl;
cout << "反序完成" << endl;
return 0;
}
看看运行结果(这是我人生中的第一个完整的链表,说实话我好激动......):
注意两个生成链表的方法是输入关键值的时候停止。
好了,终于写完了。只要肯花时间,只要问题不太难,我相信我们都能解决!
版权声明:本文为weixin_41423387原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。