到现在为止,看过的书+代码有一定量了,并且也参加了个比赛,给自己的总体感觉:编程需要的是灵活的头脑,书里的东西只是讲个规则、思想,其实际实现可以千差万别! 潜在的规则+灵活的思维 = 程序!
在做面试题5时,发现Utilities文件夹下的内容太好了,基本上是那些数据结构的实现:链表、二叉树、树等(缺个图),准备通过学习这些代码来深入理解各个数据结构及其接口/成员函数。
拿该题来说,解决单链表!!!
题目:逆序打印单链表
一、自己写单链表类及其操作
花了一下午时间,无语了。。。
1、List.h
//单链表数据结构及基本操作 //最大节点数 const int MAXLENGTH = 50; //节点 struct ListNode { int m_Value; ListNode* m_pNext; }; //List类 class List { private: ListNode LNode; int MaxLength; public: //constructor and deconstructor List(); virtual ~List(); //单链表操作 //1. 创建节点 ListNode* CreateListNode(int value); //2. 创建链表或者将节点加到链尾 void CreateFrontOrAddToTail(ListNode** pHead, int value); //3. 在第i个节点之前插入新的节点 void ListInsert(ListNode* pHead, int i, int value); //4. 删除value=e的节点 void RemoveListNode(ListNode** pHead,int value); //5. 查找第一个value=e的节点 int FindListNode(ListNode* pHead,int value); //6. 销毁链表 void DestoryList(ListNode* pHead); //7. 链表元素个数 int ListLength(ListNode* pHead); };
2、List.cpp
//List.cpp #include "List.h" #include <iostream> List::List() { LNode.m_Value = 0; LNode.m_pNext = NULL; MaxLength = MAXLENGTH; } List::~List() { } //1. 创建节点 ListNode* List::CreateListNode(int value) { ListNode* pNew = new ListNode; pNew->m_Value = value; pNew->m_pNext = NULL; } //2. 创建链表或者将节点加到链尾 void List::CreateFrontOrAddToTail(ListNode** pHead, int value) { if(pHead == NULL) return; ListNode* pNew = CreateListNode(value); if(*pHead == NULL) //空链表 *pHead = pNew; else { ListNode* pNode = *pHead; while(pNode->m_pNext != NULL) pNode = pNode->m_pNext; pNode->m_pNext = pNew; } } //3. 在第i个节点之前插入新的节点 void List::ListInsert(ListNode* pHead, int i, int value) { if(pHead == NULL || i >= MaxLength) return; ListNode* pNew = CreateListNode(value); ListNode* pNode = *pHead; ListNode* preNode = *pHead; i--; while(i && pNode->m_pNext != NULL) { preNode = pNode; pNode = pNode->m_pNext; i--; } if(i == 0) { preNode->m_pNext = pNew; pNew->m_pNext = pNode; } else cout<<"Failed!"<<endl; } //4. 删除value=e的第一个节点 bool List::RemoveListNode(ListNode** pHead,int value) { if(pHead == NULL || *pHead == NULL) return false; ListNode* pToBeDeleted = NULL; if((*pHead)->m_Value == value) { pToBeDeleted = *pHead; *pHead = (*pHead)->m_pNext; } else { ListNode* pNode = *pHead; while(pNode->m_pNext != NULL && pNode->m_pNext->m_Value != value) pNode = pNode->m_pNext; if(pNode->m_pNext != NULL && pNode->m_pNext->m_Value == value) { pToBeDeleted = pNode->m_pNext; pNode->m_pNext = pNode->m_pNext->m_pNext; } } if(pToBeDeleted != NULL) { delete pToBeDeleted; pToBeDeleted = NULL; return true; } return false; } //5. 查找第一个value=e的节点,返回其位置 int List::FindListNode(ListNode* pHead,int value) { int i = 1; ListNode* pNode = pHead; while(pNode != NULL && pNode->m_Value != value) { i++; pNode = pNode->m_pNext; } if(pNode != NULL && pNode->m_Value == value) return i; else return 0; } //6. 销毁链表 void List::DestoryList(ListNode* pHead) { ListNode* pNode = pHead; while(pHead != NULL) { pNode = pHead; pHead = pHead->m_pNext; delete pNode; } } //7. 链表元素个数 int List::ListLength(ListNode* pHead) { ListNode* pNode = pHead; int i = 0; while(pNode != NULL) { i++; pNode = pNode->m_pNext; } return i; }
未测试过。。。因为想继续做面试题5了,进度太慢了!!!
二、面试题5整理:
逆序打印单链表,首先想到的便是通过扫描链表,并同时用栈存储每个值,最后利用栈的LIFO特点打印即可。但是,鉴于自己的水平,并没有用过C++中stl里的stack,因此想了另一种方法,即将扫描链表时所得到的值存入到一个数组中,最后逆序打印即可。这种方法固然可行,但效率上必然低于栈的。所以综合以上分析,又学习了stl中的stack,具体见上一篇博客。最后,怎么也没想到可以用递归的方式求解。。。
根据作者代码思想,做了如下改动:
void PrintListReversingly_Iteratively(ListNode* pHead) { std::stack<int> nodes; ListNode* pNode = pHead; while(pNode != NULL) { nodes.push(pNode->m_nValue); pNode = pNode->m_pNext; } int value; while(!nodes.empty()) { value = nodes.top(); printf("%d ", value); nodes.pop(); } }
这里将stack的元素设置为了m_nValue的类型,仔细想想后,认为还是原代码更合理,原因如下:
原代码中,节点地址入栈,因此栈中每个元素所占存储是固定的,在32位操作系统下,指针(地址)占4字节;但如果存储的是节点数据元素,那么每个元素所占存储很可能大于4字节;
void PrintListReversingly_Recursively(ListNode* pHead) { if(pHead == NULL) { return; } else { PrintListReversingly_Recursively(pHead->m_pNext); printf("%d ", pHead->m_nValue); } }