001_链表
// 001_链表.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//#include "pch.h"#include <iostream>// // 链表的数据结构// 1. 链表是由每一个节点连接起来的.struct Node { int nData; // 数据域 Node* pNext; // 指针域,用于保存下一个节点的首地址};class List { // 记录当前链表的节点个数 int m_nCount=0; // 用于保存链表的头节点的地址 Node* m_pHead = nullptr;public: /*! * 函数名 : insert * 功 能 : 将元素nData插入到链表的第nIndex位置 * 参 数 : int nIndex 插入到的下标 * 参 数 : int nData 要插入的数据 * 返回值 : bool */ bool insert(int nIndex, int nData) { // 1. 检查插入位置是否错误. if (nIndex <0 || nIndex>m_nCount) { return false; } // 2. 在堆空间中申请内存来保存新的节点 Node* pNew = new Node; // 2.1 将要插入到链表中的数据保存在新节点. pNew->nData = nData; // 3. 在链表中找到插入位置的前一个节点. // 3.1 如果插入到的是第0个位置.那么需要将 // 新节点的首地址记录在m_pHead指针中. if (nIndex == 0) { pNew->pNext = m_pHead; m_pHead = pNew; } else { // 循环遍历到插入位置的前一个节点. Node* pPre = m_pHead; for (int i = 1; i < nIndex; ++i) { pPre = pPre->pNext; } // 4. 将前一个节点的pNext字段保存到新节点的pNext字段 // 这样就可以让新节点记录下一个节点 pNew->pNext = pPre->pNext; // 5. 将新节点的首地址保存到前一个节点的pNext字段 // 这样通过前一个节点就能找到新的节点. pPre->pNext = pNew; } // 6. 增加节点个数. ++m_nCount; } /*! * 函数名 : remove * 功 能 : 从链表中删除下标为nIndex的元素 * 参 数 : int nIndex 要删除的元素所在的下标 * 返回值 : void */ void remove(int nIndex) { // 1. 判断下标是否正确 if (nIndex < 0 || nIndex >= m_nCount) { return; } // 2. 找到要删除的元素的前一个节点 // 2.1 如果删除的是第0个节点. 这个节点的首地址 // 是保存在m_pHead头节点指针中. // 因此, 直接操作头节点指针. if (nIndex == 0) { // m_pHead->pNext记录的是被删除节点的下一个 // 节点的首地址 // 现在将下一个节点作为新的头节点:将节点的地址 // 记录在头节点指针中. Node* temp = m_pHead; m_pHead = m_pHead->pNext; delete temp; } else { Node* pPreNode = m_pHead; for (int i = 1; i < nIndex; ++i) { pPreNode = pPreNode->pNext; } // 3. 通过前一个节点的pNext字段找到被删除节点 // 的首地址 Node* pRemoveNode = pPreNode->pNext; // 4. 再通过被删除节点的pNext字段,得到被删除 // 节点的下一个节点的首地址 Node* pNextNode = pRemoveNode->pNext; // 5. 将被删除节点的下一个节点的首地址保存到 // 被删除节点的前一个节点的pNext字段. pPreNode->pNext = pNextNode; // 6. 释放掉被删除节点的内存空间 delete pRemoveNode; } // 7. 递减当前节点个数 --m_nCount; } /*! * 函数名 : find * 功 能 : 在链表中查找元素 * 参 数 : int nData 被查找的元素 * 返回值 : int 元素在链表中的下标,找不到了就返回-1 */ int find(int nData) { Node* pNode = m_pHead; int nIndex = 0; while (pNode != nullptr) { // 判断当前遍历到的节点保存的数据 // 是否是要查找的数据 if (pNode->nData == nData) { // 如果是,就返回下标 return nIndex; } // 递增下标,用于记录,当前遍历到了第几个元素 ++nIndex; // 找到下一个节点 pNode = pNode->pNext; } return -1; } /*! * 函数名 : at * 功 能 : 获取指定下标的元素的数据域的引用. * 参 数 : int nIndex 元素所在的下标 * 返回值 : int& 返回数据的引用.这样就可以在函数外部修改节点的数据了 */ int& at(int nIndex) { if (nIndex < 0 || nIndex >= m_nCount) { throw std::exception("下标越界"); } // 遍历到指定下标处的节点 Node* pNode = m_pHead; for (int i = 0; i < nIndex; ++i) { pNode = pNode->pNext; } // 返回节点的引用(返回值是int&引用类型) return pNode->nData; } /*! * 函数名 : push_back * 功 能 : 将数据插入到链表最后一个位置 * 参 数 : int nData * 返回值 : void */ void push_back(int nData) { insert(m_nCount, nData); } /*! * 函数名 : pop_back * 功 能 : 将最后一个元素的值输出到*pData,然后删除 * 参 数 : int * pData * 返回值 : void */ void pop_back(int* pData) { if (m_nCount) { // 先获取最后一个元素的值,并保存到 // 指针指向的内存中 *pData = at(m_nCount - 1); // 删除该元素 remove(m_nCount - 1); } } /*! * 函数名 : push_front * 功 能 : 将数据插入到链表的最前面 * 参 数 : int nData * 返回值 : void */ void push_front(int nData) { insert(0, nData); } /*! * 函数名 : pop_front * 功 能 : 删除链表最前面的数据, 删除前,将值输出到*pData * 参 数 : int * pData * 返回值 : void */ void pop_front(int * pData) { if (m_nCount) { // 先获取最后一个元素的值,并保存到 // 指针指向的内存中 *pData = at( 0 ); // 删除该元素 remove( 0 ); } } /*! * 函数名 : print * 功 能 : 打印链表中的所有元素 * 返回值 : void */ void print() { Node* pNode = m_pHead; printf("["); while (pNode != nullptr) { // 打印节点的数据域 printf("%d,", pNode->nData); // 找到下一个节点 pNode = pNode->pNext; } printf("\b]\n"); }};int main(){ List l; printf("测试插入功能:\n"); l.insert(0, 10); l.insert(0, 9); l.insert(2, 12); l.insert(1, 11); l.print(); // 修改链表的下标为2的项 printf("\n测试修改功能:\n"); int& rEle = l.at(2); printf("下标为2项的值:%d\n", rEle); rEle = 100; printf("下标为2项的值:%d\n", rEle); printf("\n测试查找功能:\n"); int nIndex = l.find(12); printf("元素12在链表中的第%d项\n", nIndex); printf("\n测试删除功能:\n"); // 删除元素9 // 1. 先通过find查找到元素9的下标 // 2. 在通过删除函数删除指定下标的元素 l.remove(l.find(9)); l.remove(3);// 9- 11 - 10 -12 l.print(); l.remove(1); l.print(); l.remove(0); l.print(); // 模拟栈的操作: printf("\n模拟栈操作:\n"); int nData = 0; l.push_back(10); l.push_back(9); l.push_back(8); l.push_back(7); l.print(); l.pop_back(&nData); l.print(); l.pop_back(&nData); l.print(); l.pop_back(&nData); l.print(); l.pop_back(&nData); l.print(); // 模拟队列的操作 printf("\n模拟队列操作:\n"); l.push_back(100); l.push_back(101); l.push_back(102); l.push_back(103); l.pop_front(&nData); l.print(); l.pop_front(&nData); l.print(); l.pop_front(&nData); l.print(); l.pop_front(&nData); l.print();}
二分法
// 二分法.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//#include "pch.h"#include <iostream>int bin_seach(int* pArr, int nCount, int nData){ //1. 先确定开始下标和结束的下标 int nBeg = 0; int nEnd = nCount - 1; int nMid = (nEnd - nBeg) / 2; int i = 1; while (1) { // 判断中间位置保存的数据是否就是 // 要查找的元素 if (pArr[nMid] == nData) { return nMid; } if (nBeg == nEnd) { return -1; } // 开始确定下一次的查找范围 if (nData < pArr[nMid]) { // 1. 保存开始下标不变 // 2. 将当前中间下标设置为新的结束下标 nEnd = nMid; } else { // 1. 保存结束的下标不变 // 2. 将当前中间下标设置为新的开始下标 nBeg = nMid; } // 重新计算新的中间下标 nMid = ((nEnd - nBeg) / 2) + nBeg; printf("循环了%d次\n", i++); }}void sort(int *pArr, int nCount) { for (int i = 0; i < nCount - 1; ++i) { for (int j = 0; j < nCount - i - 1; ++j) { if (pArr[j] > pArr[j + 1]) { int temp = pArr[j]; pArr[j] = pArr[j + 1]; pArr[j + 1] = temp; } } }}void printArr(int *pArr, int nCount) { for (int i = 0; i < nCount; ++i) { printf("%d,", pArr[i]); } printf("\b\n");}int main(){ int nArr[] = { 10,5,6,8,9,45,65,32,45 }; printArr(nArr, _countof(nArr)); sort(nArr, _countof(nArr)); printArr(nArr, _countof(nArr)); int nIndex = bin_seach(nArr, _countof(nArr), 45); printf("45在%d\n", nIndex);}