C++ Primer Plus第十章第8个编程题解答(链表实现,仅参考)

1. 编程题目概况

c++是一门庞大而复杂的语言, 像个巨人. 里面有太多让人不容易理解的抽象概念, 掌屋更是很难. c++ primer plus和c primer plus的作者都是普拉达教授, 个人觉得比较适合初学者, 设计的编程题也很有针对性. 

第10章主要讲了对象和类, 编程题的最后一道题, 原文如下:

8.可以将简单列表描述成下面这样:

a. 可存储0或多个某种类型的列表

b. 可创建空列表

c. 可在列表中添加数据项

d. 可确定列表是否为空

e. 可确定列表是否为满

f. 可访问列表中的每一个数据项, 并对它执行某种操作.

可以看到这个列表确实很简单, 例如, 它不允许插入或删除数据项.

可以选择使用数组或链表来实现该列表, 但公有接口不应依赖于所做的选择.也就是说, 公有接口不应有数组索引, 节点指针等. 应使用通用的概念来表达创建列表, 在列表中添加数据项等操作.

这里第 f 项要求用函数指针实现对所有列表成员应用某种操作, 函数指针 理解稍有难度. 

我用了链表来实现这个列表, 除了数组, 链表 也是可以的. 另外, 还实现了  "删除"  和  "插入"  操作.

当然我的算法可能会有错误, 欢迎指正交流, 写得可能也不太好, 这里只是提供一种方式供初学者参考, 代码中都有详细的注释供参考.

1.1 一共有三个文件, 先是头文件 list_linkedlist.h

#ifndef LIST_LINKEDLIST_H
#define LIST_LINKEDLIST_H

typedef int Item;

class List
{
private:                                 
	enum {MAX = 10};           /*列表上限*/
	struct node                /*列表成员节点结构*/
    {
        Item member;           /*列表数据*/
	    struct node *next;     /*指向下一个列表成员*/
    };
    struct node *head;         /*存放列表第一个成员指针*/
public:
	List();                    /*默认构造函数*/
	List(const unsigned int n);/*带参构造函数*/
	~List();                   /*析构函数,清除列表*/
	bool isempty() const;      /*查看列表是否为空*/
	bool isfull() const;       /*查看列表是否已满*/
	bool add(const Item &it);  /*添加列表成员*/
    bool del(const Item &it);  /*删除列表成员*/
    bool insert(const Item &it, const int index = -1); /*插入节点*/
	void visit(void (*pf)(Item &it)); /*遍历所有成员并传给函数指针指向的函数*/
};
#endif

1.2. 然后是类实现文件 list_linkedlist.cpp

#include <iostream>
#include "list_linkedlist.h"

using std::cout;
using std::cin;
using std::endl;

/*默认构造函数*/
List::List()
{   
	/*默认构造函数不创建节点,成员数为0*/
    head = nullptr;   
}

/*带参构造函数*/
List::List(const unsigned int n)
{   
    unsigned int i;
    node *temp, *lastnd;
    head = temp = lastnd = nullptr;/*初始化指针*/   
    Item tp;

	/*循环输入列表成员*/
    for (i = 0; i < n && i < MAX; i++)
    {
        cout << "Please enter #" << (i + 1)
            << "'s members of the list: ";
        cin >> tp;        
        if (!cin) /*若输入错误字符*/
        {
            cin.clear(); /*输入错误要重置cin输入状态标记位*/
            while (cin.get() != '\n') /*清除多余输入*/
                continue;
            cout << "Bad input, program terminated.\n";
            break;  /*中断输入*/
        }
        while (cin.get() != '\n') /*输入正确后也要清除多余输入*/
            continue;
        /*如果是第一次创建则创建head(头)节点*/
        if (this->isempty())      /*若当前列表为空*/
        {
            temp = new node;      /*创建第一个节点*/
            temp->member = tp;
            temp->next = nullptr;
            head = temp;
        }
        else/*否则在末节点后创建新节点*/
        {   /*找到末节点后将新节点添加到最后位置成为新*/
            /*末节点,并把原末节点next指向新末节点*/
            lastnd = head;
            while (lastnd->next != nullptr) /*找到末节点*/
                lastnd = lastnd->next;
            temp = new node;
            temp->member = tp; /*列表成员存入新节点*/
            temp->next = nullptr; /*成为新末节点*/
            lastnd->next = temp; /*原末节点next指向新末节点*/
        }
    }
}

/*析构函数*/
List::~List()
{
    node *delnode = nullptr;
    if (!(this->isempty()))
    {
        /*head控制后面一个的列表成员位置*/
        /*delnode标识当前要被删除的列表成员位置*/
        delnode = head;
        while (head->next != nullptr)
        {
            head = head->next; /*指向下一个节点*/
            delete delnode;    /*释放当前节点*/
            delnode = head;    /*更新当前节点*/
        }
        delete delnode;        /*释放最后一个节点*/
        delnode = nullptr;     
        head = nullptr;       
    }
}

/*判断列表是否为空函数*/
bool List::isempty() const
{
    return head == nullptr;
}

/*判断列表是否已满函数*/
bool List::isfull() const
{
	int count = 0;
    /*如果是空列表(未满)*/
    if (this->isempty())
        return false;
    else
    {   /*否则统计列表成员数(节点数)*/
        /*与最大值MAX比较*/
        node *temp = head;
        count++;
        while (temp->next != nullptr)
        {
            count++;
            temp = temp->next;
        }
        return count == MAX;
    }
}

/*添加列表成员*/
bool List::add(const Item &it)
{
	/*如果是列表已满则不执行添加操作*/
    if (this->isfull())
	{
        cout << "\nThe list is full, no more items can be added.\n";
        return false;
    }
	else if (this->isempty())
    {   /*如果列表为空则创建新节点,将列表成员值*/
        /*存入head节点,然后head节点next置空为末节点*/
        node *temp = new node;
        temp->member = it;
        temp->next = nullptr;
        head = temp;
    }
    else
    {  /*如果列表不为空则先找到末节点再创建新节点*/
       /*并将新节点加入链表同时设置为新末节点*/
       node *lastnd = head;
        while (lastnd->next != nullptr)
            lastnd = lastnd->next;
        node *temp = new node;
        temp->member = it;
        temp->next = nullptr;
        lastnd->next = temp;
    }
    cout << "\nThe member of the list added successfully.\n";
    return true;
}

/*删除列表成员*/
bool List::del(const Item &it)
{
    bool delflag = false;
    if (head == nullptr)
    {   /*如果是空列表则不执行删除*/
        cout << "The list is empty, deletion failed.\n";
        return false;
    }
    else
    {   /*否则列表不为空*/
        node *delnode = nullptr;
        node *prenode = nullptr;
        /*如果要删除的是列表成员是第一个成员*/
        if (head->member == it)
        {   /*分两种情况,第一种如果只有一个列表成员*/
            /*则删除head节点并置空*/
            if (head->next == nullptr)
            {
                delete head;
                head = nullptr;
                cout << "\nThe only list member was deleted successfully.\n";
            }
            else if (head->next != nullptr)
            {   /*第二种如果后面还有列表成员*/
                /*删除head节点,head指向第二个节点*/
                delnode = head;
                head = head->next;
                delete delnode;
                delnode = nullptr;
                cout << "\nThe first member of list was deleted successfully.\n";
            }
        }
        else /*否则要删除的是非第一个列表成员*/
        {
            delnode = head;
            prenode = head;
            while (delnode->next != nullptr)
            {   /*如果找到要删除的列表成员*/
                if (delnode->member == it)
                {
                    /*则将要删除节点的上一个节点指向要删除*/
                    /*节点的下一个节点(跳过要被删除的节点)*/
                    prenode->next = delnode->next;
                    delete delnode; /*删除找到的节点*/
                    delnode = nullptr;
                    cout << "\nThe member of the list was deleted successfully.\n";
                    delflag = true;
                    break;
                }
                /*prenode保存要删除节点的上一个节点位置*/
                prenode = delnode;
                delnode = delnode->next;
            }
            /*循环退出时,有两种情况:第一种,没成功执行删除操作(delfalg = false),则delnode
              已指向最后一个列表成员,那么与最后一个列表成员比对,若比对成功则要删除最后一
              个列表成员则先删除末节点,再设置倒数第二个列表成员为新末节点;否则提示没有
              找到要删除的列表成员。第二种,已经执行过删除操作(delflag = true),则不再删除
              最后一个列表成员操作*/
            if (delflag == false && delnode->member == it)
            {
                delete delnode;
                delnode = nullptr;
                prenode->next = nullptr;
                cout << "\nThe last member of the list was deleted successfully.\n";
            }
            else if (delflag == 0)
            {
                cout << "\nCouldn't find '" << it
                    << "' in the list, deletion failed.\n";
                return false;
            }
        }
    }
    return true;
}

/*插入节点函数*/
/*这里index设定了默认值-1,表示如果不指定index则在*/
/*末尾插入*/
bool List::insert(const Item &it, const int index)
{   
	bool istflag = false;
    int location = 0;
    node *temp = nullptr;    /*存放要插入的新列表成员*/
    node *prnode = nullptr;  /*保存要插入节点的上一个节*/
    node *istnode = nullptr; /*点位置要插入列表成员的位置*/
    /*如果列表已满则不执行插入*/
    if (this->isfull())
    {
        cout << "The list is full, no more members can be inserted.\n";
		return false;
    }
    else if (this->isempty())
    {   
		/*如果为空列表则插入第一个列表成员*/
        add(it);        
    }
    else
    {
        /*否则列表有空位且有列表数据,如果要插入
          的位置在head节点(索引值为0)则创建新成员
          并将要插入的成员节点设置为新head节点*/
        if (index == 0)
        {
            temp = new node;
            temp->member = it;
            temp->next = head;  /*新节点next指向head节点*/
            head = temp; /*(新节点加入链表)新节点成为新的head节点*/
            cout << "\nThe members of the list was inserted successfully.\n";
        }
        else if (index < 0 || index >= MAX)
        {
            /*索引值为负数或大于等于MAX则*/
            /*直接在最后插入列表成员*/
            cout << "\nPosition index error "
                << "the list member will be inserted at the end.\n";
            add(it);
        }
        else /*这个else代码块中要插入的节点位置索引值都在2~MAX-1*/
        {    /*如不在头节点插入则查找列表成员位置,找到后创建新
               节点并将新节点的next指向要插入位置的节点然后将要
               插入节点的位置的前一个节点的next指向新创建的节点*/
            prnode = head;
            istnode = head;
            while (istnode->next != nullptr)
            {
                prnode = istnode; /*保存当前节点位置到prnode*/
                istnode = istnode->next; /*移动当前节点到下一节点*/
                location++;          /*移位置索引增加*/
                if (index == location) /*如果是用户指定位置*/
                {
                    temp = new node;  /*创建新节点*/
                    temp->member = it; /*存放用新列表成员*/
                    /*新节点next指向要插入位置的节点*/
                    temp->next = prnode->next;
                    /*要插入节点位置的上一节点的next指向新节点(实现插入操作)*/
                    prnode->next = temp;
                    cout << "\nThe member of the list was inserted successfully.\n";
					istflag = true;
                    break;
                }
            }
			/*如果没在正常index插入,则在末尾插入*/
            if (istflag == false)
            {
				cout << "\nPosition index error "
					<< "the list member will be inserted at the end.\n";
                add(it);
            }
		}
    }
    return true;
}

/*遍历函数*/
/*函数指针是一个C/C++中很重要的概念,它和其它数据类型*/
/*指针一样,只保存一个地址,只不过这个保存的这个地址是*/
/*函数的地址,而函数的地址就是函数名,函数名就是个地址*/
/*常量,和数组名一样就是个地址常量,将函数名传给函数指*/
/*针,那么在这个使用函数指针的地方可以用这个指针来调用*/
/*这个函数,只要符合这个函数指针的类型,就可以把相同类*/
/*型的函数传给这个函数指针*/
/*这里是给pf函数指针指向的函数传递所有的列表成员*/
void List::visit(void (*pf)(Item &))
{
	if (this->isempty())
		cout << "The list is empty, can't visit.\n";
 	else
    {
        node *temp = head;
        while (temp->next != nullptr)
        {
			/*pf调用函数,给被调函数传递所有列表成员*/
            (*pf)(temp->member); 
            temp = temp->next;
        }
        (*pf)(temp->member);
    }
}

1.3. 最后是主程序文件 uselist_linkedlist.cpp

#include <iostream>
#include "list_linkedlist.h"

/*打印成员*/
void print(Item &it);
/*成员乘法运算*/
void multiply(Item &it);
/*提示信息*/
void promptmsg(const List &ls);

using std::cout;
using std::endl;

int main()
{
	/*测试自定义列表*/
    int i;
	/*测试数据*/
    int num[10] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 101};

	/*声明一个列表*/
    List listb;
	/*检查成员函数isempty是否正常*/
	cout << "list.isempty() = " << listb.isempty() << endl;
	/*根据列表状态打印提示信息*/
    promptmsg(listb);
	/*用visit函数遍历所有列表成员*/
	/*并作为visit参数中的函数指针指*/
	/*向的函数的参数*/
    listb.visit(print); /*对所有成员执行打印*/
	
	/*添加列表成员测试*/
    for (i = 0; i < 8; i++) 
	    listb.add(num[i]);
	/*再次打印所有成员*/
    listb.visit(print);
	/*添加成员*/
	listb.add(222);
	/*指定位置插入成员*/
	listb.insert(103, 3);
	/*指定位置插入成员*/
	listb.insert(333, 8);
	/*打印所有成员*/
    listb.visit(print);
	/*检查提示*/
    promptmsg(listb);
	/*对所有成员执行乘法*/
	listb.visit(multiply);
	/*打印所有成员*/
    listb.visit(print);

	cout << "\n\nDone.\n";
    return 0;
}

/*打印成员*/
void print(Item &it)
{
	cout << it << " ";
}

/*成员乘法运算*/
void multiply(Item &it)
{
	it *= 2;
}

/*提示信息*/
void promptmsg(const List &ls)
{
	const char *msg[3] = {
		"\nThe list is empty, members can be added.\n",
		"\nThe list is full.\n",
		"\nThe list has some members, but more can be added.\n"
	};
	if(ls.isempty())
		cout << msg[0] << endl;
	else if (ls.isfull())
		cout << msg[1] << endl;
	else
		cout << msg[2] << endl;
}

运行结果:

2. 结语

c++它不光有c中的指针, 它还有类, 还有C++的标准模板库(Standard Template Library, STL)中的各种容器, 关于类还有多重继承, 有很多复杂且容易出错的地方. 要想精通并不容易.java的设计就是摒弃了c++中的一些复杂特性而诞生的. 删除指针, 取消多重继承(但可实现多个接口), 设计垃圾回收器以免忘记释放内存等等.

喜欢这篇文章就点赞收藏, 欢迎留言交流, 您的点赞和收藏就是我写文章的动力. 谢谢.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值