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);
}