/*
总结一下几个重点
1、malloc\free null的使用
2、结构体池
3、实现链表、队列、栈、树

队列是先入先出的数据结构
栈的行为是先进后出
*/
#include<iostream>
#include <windows.h>
using namespace std;

/************************************************************************/
/* 指针和结构体 */
/************************************************************************/
/*
指针和结构体 ,本篇目录
一、介绍
二、结构体释放问题
三、避免malloc/free开销
四、用指针支持数据结构
五、小结

*/

/*
一、介绍
本章重点:
1、结构体声明,
2、结构体访问
3、结构体内存
*/

/*
声明结构体的两个主要方式:
1、用struct关键字声明
*/
/*
struct _person
{
char * pszFirstName;
char* pszLastName;
char* pszTitle;
unsigned int iAge;

} stPerson;
*/
//2、用struct关键字
typedef struct _person
{
char * pszFirstName;
char* pszLastName;
char* pszTitle;
unsigned int iAge;
}Person;
/************************************************************************/
/* 关于结构体示例的声明 */
/************************************************************************/

// person的示例声明如下
Person person;

// 也可以这样声明一个指针,然后分配内存
Person *ptrPerson =(Person*)malloc(sizeof(Person));

/* 访问方式主要有三个,结构体变量一个,结构体指针两个
1、结构体变量
2、结构体指针
3、结构体指针解引用
*/
void VisitStructWay()
{
// 1、结构体变量
person.pszFirstName = (char*)malloc(strlen("Emily") + 1);
strcpy(person.pszFirstName, "Emily");
person.iAge = 23;

// 2、结构体指针
ptrPerson->pszFirstName = (char*)malloc(strlen("Emily") + 1);
strcpy(ptrPerson->pszFirstName, "Emily");
ptrPerson->iAge = 23;

// 3、结构体指针解引
(*ptrPerson).pszFirstName = (char*)malloc(strlen("Emily") + 1);
strcpy((*ptrPerson).pszFirstName, "Emily");
(*ptrPerson).iAge = 23;

}
/*
为结构体分配内存
涉及到内存对齐的问题,后面详细介绍
*/

/************************************************************************/
/* 二、结构体释放问题 */
/************************************************************************/

// 下面这个函数在结构体的初始化阶段,会为每个字段赋值,对于指针字段,我们
//会从堆上分配内存,并把地址赋值给每个指针
void initializePerson(Person *ptrPerson,const char* pszFn,const char* pszLn,
const char* pszTitle,unsigned int iAge)
{
ptrPerson->pszFirstName = (char*)malloc(strlen(pszFn) + 1);
strcpy(ptrPerson->pszFirstName, pszFn);

ptrPerson->pszLastName = (char*)malloc(strlen(pszLn) + 1);
strcpy(ptrPerson->pszLastName, pszLn);

ptrPerson->pszTitle = (char*)malloc(strlen(pszTitle) + 1);
strcpy(ptrPerson->pszTitle, pszTitle);

ptrPerson->iAge = iAge;
}

// 释放内存
void deallocatePerson(Person *ptrPerson)
{
free(ptrPerson->pszFirstName);
free(ptrPerson->pszLastName);
free(ptrPerson->pszTitle);

}
// 下面这个函数包含了结构体指针的初始化和释放
void processPerson()
{
Person *ptrPerson;
ptrPerson = (Person *)malloc(sizeof(ptrPerson));

initializePerson(ptrPerson,"Peter","UNderwood","Manager",36);
deallocatePerson(ptrPerson);
free(ptrPerson);

}
/************************************************************************/
/* 三、避免malloc/free开销 */
/************************************************************************/
/*
重复分配然后释放结构体会产生一些开销,可能会导致巨大的性能瓶颈。
解决这个问题的技术思想是:
为分配的结构体单独维护一个表,当用户不再需要某个结构体实例的时候,将其返回到结构体池中,
当我们需要某个实例的时候,从结构体池中获取一个对象。如果池中没有可用的元素,我们就动态的
分配一个实例。
这种方法的好处是能够高效地维护一个结构体池,能够按需使用和重复使用内存
*/

// 下面是实现方法
#define D_LIST_SIZE 10
Person *list[D_LIST_SIZE];

// 使用之前先初始化
void initializeList()
{
for(int i = 0;i<D_LIST_SIZE;i++)
{
list[i] = NULL;
}
}

// 下面用两个函数来添加和获取结构体
// 这个函数获取一个非空的结构体
Person *getPerson()
{
for(int i = 0;i < D_LIST_SIZE; i++)
{
if(NULL != list[i])
{
Person *ptr = list[i];
list[i] = NULL;

return ptr; // 返回非空的结构体
}
}

Person *ptrPerson = (Person *)malloc(sizeof(Person));

return ptrPerson;
}

// 这个函数要么将结构体返回表,要么把结构体释放掉
Person *returnPerson(Person *ptrPerson)
{
for(int i = 0; i < D_LIST_SIZE; i++)
{
if(NULL == list[i])
{
list[i] = ptrPerson;

return ptrPerson;
}
}

deallocatePerson(ptrPerson);
free(ptrPerson);

ptrPerson = NULL;

return NULL;
}

// 下面函数说明了表的初始化,以及如何将一个结构体添加到表中
void TestStructPool()
{
initializeList();
Person *ptrPerson;
ptrPerson = getPerson();
initializePerson(ptrPerson, "Mark", "Kevin", "Smith", 35);

returnPerson(ptrPerson);
}
/*
结构体池,有个问题,就是表长度,如果表太短,就需要频繁分配并释放内存
如果表太而没有使用结构体,那么就会浪费大量内存,也不能用在其它地方。
管理表的长度,可以用更复杂的表管理策略来做
*/

/************************************************************************/
/* 四、用指针支持数据结构
/************************************************************************/
/*
本章分为四个部分
1、单链表
2、用指针支持队列
3、用指针支持栈
4、用指针支持树

指针的便利性来自于两个方面:1、动态内存分配,2、自动切换指针引用的便利性
链表是非常有用的数据结构,可以作为实现队列和栈的基础
下面用两个函数和两个函数指针说明下
*/
typedef struct _employee
{
char name[32];
unsigned char age;
}Employee;

// 比较
int CompareEmployee(Employee* ptrEm1, Employee* ptrEm2)
{
int iSize = strcmp(ptrEm1->name, ptrEm2->name);

return iSize;
}

// 输出
void displayEmployee(Employee* ptrEmployee)
{
printf("%s\t%d\n",ptrEmployee->name,ptrEmployee->age);
}

typedef void(*DISPLAY)(void * );
typedef int(*COMPARE)(void* , void*);

void TestPtrStruct()
{
Employee em1;
Employee em2;

strcpy(em1.name, "KAKA");
em1.age = 22;

strcpy(em2.name, "KAKA");
em2.age = 22;

int iResult = CompareEmployee(&em1, &em2);

cout << "iResult = "<< iResult<< endl;


displayEmployee(&em1);
}
//(一)、单链表
/*
链表有很多类型,最简单的是单链表,一个节点到下一个节点只有一个链接
链接从头结点开始,到尾节点结束
循环链表没有尾节点,链表的最后一个节点又指向头节点
双链表用了两个链表,一个向前链接,一个向后链接

下面介绍单链表
*/
// 节点结构体
typedef struct _node
{
void *data; // 持有任意类型数据
struct _node *next; // 指向下一节点的指针
}Node;

// 链表结构体
typedef struct _linkedList
{
Node *pHead;
Node *pTail;
Node *pCurrent;
} LinkedList;

// 初始化链表
void initializeList(LinkedList *pList);

// 给链表头结点添加数据
void addHead(LinkedList *pList,void* pData);

// 给链表尾节点添加数据
void addTail(LinkedList *pList, void* pData);

// 从链表删除节点
void deleteNode(LinkedList *pList, Node* pNode);

// 返回包含指定数据的节点指针,找到指定节点
Node *getNode(LinkedList *pList,COMPARE,void* pData);

// 打印链表
void displayLinkedList(LinkedList *pList,DISPLAY);

void initializeList(LinkedList *pList)
{
pList->pHead = NULL;
pList->pTail = NULL;
pList->pCurrent = NULL;
}

void addHead(LinkedList *pList, void* pData)
{
Node *pNode = (Node*)malloc(sizeof(Node));
pNode->data = pData;
if(NULL == pList->pHead)
{
pList->pTail = pNode;
pNode->next = NULL;
}
else
{
pNode->next = pList->pHead;
}

pList->pHead = pNode;
}

void addTail(LinkedList *pList, void* pData)
{
Node *pNode = (Node*)malloc(sizeof(Node));
pNode->data = pData;
pNode->next = NULL;
if(NULL == pList->pHead)
{
pList->pHead = pNode;
}
else
{
pList->pTail->next = pNode;
}

pList->pTail = pNode;
}

void deleteNode(LinkedList *pList, Node* pNode)
{
if(pNode == pList->pHead)
{
if(NULL == pList->pHead->next)
{
pList->pHead = pList->pTail = NULL;
}
else
{
pList->pHead = pList->pHead->next;
}
}
else
{
Node *pTemp = pList->pHead;
while(pTemp != NULL && pTemp->next != pNode)
{
pTemp = pTemp->next;
}
if(pTemp != NULL)
{
pTemp->next = pNode->next;
}
}

free(pNode);
}


Node * getNode(LinkedList *pList, COMPARE compare, void* pData)
{
Node *pNode = pList->pHead;
while(NULL != pNode)
{
if(0 == compare(pNode->data,pData))
{
return pNode;
}

pNode = pNode->next;
}
return NULL;
}

void displayLinkedList(LinkedList *pList, DISPLAY display)
{
printf("\nLinked List\n");

Node *pCurrent = pList->pHead;
while(NULL != pCurrent)
{
display(pCurrent->data);
pCurrent = pCurrent->next;
}
}

// 链表测试
void TestLink()
{
LinkedList pLinkedList;

Employee *pSamuel = (Employee *)malloc(sizeof(Employee));
strcpy(pSamuel->name, "Samuel");
pSamuel->age = 32;

Employee *pSally = (Employee *)malloc(sizeof(Employee));
strcpy(pSally->name, "Sally");
pSally->age = 22;

Employee *pSusan = (Employee*)malloc(sizeof(Employee));
strcpy(pSusan->name, "Susan");
pSusan->age = 24;

initializeList(&pLinkedList);

addHead(&pLinkedList, pSamuel);
addHead(&pLinkedList, pSally);
addHead(&pLinkedList, pSusan);

Node *pNode = getNode(&pLinkedList, (int(*)(void*, void*))CompareEmployee, pSally);

cout << pNode->data<< endl;
cout << "Test Show"<< endl;
displayLinkedList(&pLinkedList,(DISPLAY)displayEmployee);

deleteNode(&pLinkedList,pNode);
}
/*
(二)、队列
队列的基本操作有两种,enqueue(入队),它是在表的末端(称为队尾)插入一个元素;
dequeue(出队),它是删除(并返回)表的开头的元素

先入先出的数据结构
实现队列经常用到链表,入队操作就是将节点添加链表一端,出队操作就是从链表另一端取节点
,并删除节点
(下面这个实现不是很好)
*/
typedef LinkedList Queue;

// 初始化队列
void initializeQueue(Queue * pQueue)
{
initializeList(pQueue);
}

// 向队列增加一个节点
void enQueue(Queue * pQueue,void *pNode)
{
addHead(pQueue, pNode);
}

// 从队列中取数据,并从队列中删除这个数据
void *deQueue(Queue *pQueue)
{
Node *tmp = pQueue->pHead; // 头指针
void * pData;

if(NULL == pQueue->pHead)
{
pData = NULL;
}
else if (pQueue->pHead == pQueue->pTail)
{
pQueue->pHead = pQueue->pTail = NULL;
pData = tmp->data;

free(tmp);
}
else
{
while(tmp->next != pQueue->pTail)
{
tmp = tmp->next;
}
}

pQueue->pTail = tmp;
tmp = tmp->next;
pQueue->pTail->next = NULL;
pData = tmp->data;

return pData;
free(tmp);
}

// 队列测试方法
void TestQueue()
{
Employee *pJeff = (Employee *)malloc(sizeof(Employee));
strcpy(pJeff->name, "Jeff");
pJeff->age = 32;

Employee *pJames = (Employee *)malloc(sizeof(Employee));
strcpy(pJames->name, "James");
pJames->age = 22;

Employee *pMark = (Employee*)malloc(sizeof(Employee));
strcpy(pMark->name, "Mark");
pMark->age = 24;


Queue qQueueTest;
initializeList(&qQueueTest);

enQueue(&qQueueTest, pJeff);

enQueue(&qQueueTest, pJames);

enQueue(&qQueueTest, pMark);

void *pData = deQueue(&qQueueTest);

char name[20];
strcpy(name, ((Employee*)pData)->name);

cout << name<< endl;
}
/*
(三)、 栈
堆栈的基本操作是push(进栈)和push(出栈),前者相当于插入,后者这是删除最后插入的元素
栈的行为是先进后出
栈同样可以用链表支持操作
*/
typedef LinkedList Stack;

// 初始化栈
void initializeStack(Stack *pStack)
{
initializeList(pStack);
}

// 入栈
void push(Stack *pStack,void *pData)
{
addHead(pStack, pData);
}
/*
出栈,考虑三种情况:
1、栈为空,返回NUNULL
2、栈中有一个元素,如果节点指向尾节点,那么头结点和尾节点是同一个元素。将头结点
和尾节点置为NULL。然后返回数据

3、栈中有多个元素
头结点赋值为链表的下一个元素,然后返回数据
*/

void *pop(Stack *pStack)
{
Node * pNode = pStack->pHead;
if(NULL ==pNode)
{
return NULL;
}
else if(pNode == pStack->pTail)
{
pStack->pHead = pStack->pTail = NULL;

void * pData = pNode->data;
free(pNode);

return pData;
}
else
{
pStack->pHead = pStack->pHead->next;
void * pData = pNode->data;
free(pNode);
return pData;
}

}

void TestStack()
{
Employee *pJeff = (Employee *)malloc(sizeof(Employee));
strcpy(pJeff->name, "Jeff");
pJeff->age = 32;

Employee *pJames = (Employee *)malloc(sizeof(Employee));
strcpy(pJames->name, "James");
pJames->age = 22;

Employee *pMark = (Employee*)malloc(sizeof(Employee));
strcpy(pMark->name, "Mark");
pMark->age = 24;

Stack qStackTest;
initializeStack(&qStackTest);

push(&qStackTest, pJeff);
push(&qStackTest, pJames);
push(&qStackTest, pMark);

Employee *employee;
for(int i = 0;i<4;i++)
{
employee = (Employee*)pop(&qStackTest);
printf("Popped %s\n",employee->name );
}


}
/*
(四)、用指针支持栈
*/

typedef struct _tree
{
void *pData;
struct _tree *pLeft;
struct _tree *pRight;

} TreeNode;

void insertNode(TreeNode **pRoot,COMPARE compare ,void *pData)
{
TreeNode *pNode = (TreeNode*)malloc(sizeof(TreeNode));
pNode->pData = pData;
pNode->pLeft = NULL;
pNode->pRight = NULL;

if(NULL == *pRoot)
{
*pRoot = pNode;

return;
}

while(1)
{
if(compare((*pRoot)->pData,pData)>0)
{
if(NULL != (*pRoot)->pLeft) // 左节点有值
{
*pRoot = (*pRoot)->pLeft;
}
else
{
(*pRoot)->pLeft = pNode;

break;
}
// *pRoot = (*pRoot).pLeft;
}
else
{
if(NULL != (*pRoot)->pRight) // 右节点有值
{
*pRoot = (*pRoot)->pRight;
}
else
{
//(*pRoot) = (*pRoot)->pRight;
(*pRoot)->pRight = pNode;
break;
}
}
}
}
/*
遍历二叉树的方式有三种:前序、中序、后序。
三种遍历方式的技术步骤相同,但是顺序不同

三个步骤:访问节点、往左、往右
访问节点的三种顺序:

中序、先往左,访问节点,再往右
前序、访问节点,往左,再往右
后序、先往左,再往右,最后访问节点
*/

// 前序
void preOrder(TreeNode *pRoot, DISPLAY display)
{
if(NULL != pRoot)
{
display(pRoot->pData);
preOrder(pRoot->pLeft, display);
preOrder(pRoot->pRight, display);
}
}

// 中序;
void inOrder(TreeNode *pRoot,DISPLAY display)
{
if(NULL != pRoot)
{
inOrder(pRoot->pLeft, display);
display(pRoot->pData);
inOrder(pRoot->pRight, display);
}
}

// 后序
void postOrder(TreeNode *pRoot, DISPLAY display)
{
if(NULL != pRoot)
{
postOrder(pRoot->pLeft, display);
postOrder(pRoot->pRight, display);
display(pRoot->pData);
}
}

void TestTree()
{
Employee *pSamuel = (Employee *)malloc(sizeof(Employee));
strcpy(pSamuel->name, "Samuel");
pSamuel->age = 32;

Employee *pSally = (Employee *)malloc(sizeof(Employee));
strcpy(pSally->name, "Sally");
pSally->age = 28;

Employee *pSusan = (Employee*)malloc(sizeof(Employee));
strcpy(pSusan->name, "Susan");
pSusan->age = 45;

TreeNode *pTree = NULL;
insertNode(&pTree, (COMPARE) CompareEmployee, pSamuel);
insertNode(&pTree, (COMPARE)CompareEmployee, pSally);
insertNode(&pTree, (COMPARE)CompareEmployee, pSusan);

// 遍历
preOrder(pTree, (DISPLAY)displayEmployee);
inOrder(pTree, (DISPLAY)displayEmployee);
postOrder(pTree, (DISPLAY)displayEmployee);

}

int main()
{
TestTree();
//TestStack();
//TestQueue();
//TestLink();
//TestPtrStruct();
//TestStructPool();
// const char* pszName = "KAKA";
//
// strcpy(pszName, "JAM");

system("pause");
return 0;
}