单链表学习

链表学习

静态链表

  • 链表:结构体变量通过指针进行连接
  • 代码示例:
struct Node {
  int data;  // 数据域
  struct Node* next;  // 指针域
  // 指针域指向下一个结构体变量
};
  • 静态链表使用不是很多,通常使用能动态申请内存的动态链表

动态链表

  • 需要使用动态内存申请和模块化设计
  • 模块化设计就指的是封装操作链表的功能

内存申请 (malloc)

  • malloc函数:
void* malloc(unsigned size);
  • 其中size是需要申请的内存大小

  • 因为malloc返回的是一个无类型的指针(void*),所以使用的时候需要强制转换

  • 使用完成后需要把指针所指的内存释放掉 (free),并且指针指向空 (NULL)

  • 代码示例:

int *p = (int *)malloc(sizeof(int)*10);
// 这里申请了一段 40 个字节的内存空间,强制转换成 int 型 的指针赋给 *p
free(p);
// free使用的是首地址的指针,不能使用中间位置的指针
p = NULL;
// 一定要记得置空

创建动态链表

创建链表头
struct Node* createList() {
  struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
  // headNode 成为了结构体变量
  headNode -> next = NULL;
  return headNode;
  // 返回这个结构体指针
}

headNode

  • 链表头一般是不储存数据的
创建节点 (和创建链表头基本一样)
struct Node* createNode(int data) {
  struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
  newNode -> data = data;
  newNode -> next = NULL;
  return newNode;
}

打印节点

  • 通过指针来遍历整个链表
  • 代码示例:
// 打印节点
void printList(struct Node* headNode) {
  struct Node* pMove = headNode->next;
  while (pMove) {  // 判断条件为NULL时退出循环
    cout << pMove->data << endl;
    poMove = pMove->next;
  }
}

插入方法

  • 插入方法分为头插法和尾插法

  • 头插法:

    • 节点要插入在链表头和头节点之间的位置
    • 只需要把插入的节点的 next 指向下一个节点,头节点的 next 指向要插入的节点就可以了
  • 代码示例:

// 头插法
void insertNodeByHead(struct Node* headNode, int data) {
  // 新建插入的节点
  struct Node* newNode = createNode(data);
  newNode->next = headNode->next;
  headNode->next = newNode;
}

插入节点

  • 尾插法更简单,只需要在最后一个节点上连接要插入的节点就可以了

指定位置删除

  • 越过要删除的节点,直接将上一个节点指向下一个节点
// 删除节点
void deleteNodeByAppoin(struct Node* headNode, int posData) {
 struct Node* posNode = headNode->next; // 要删除的节点的指针
  struct Node* posNodeFront = headNode;  // 要删除的节点的前一个指针
  // 判断链表是否为空
  if (posNode == NULL)
    cout << "链表为空,无法查找" << endl;
  else
  {
    // 开始寻找
    while (posNode->data != posData) {
      posNodeFront = posNode;
      posNode = posNode->next;
      // 如果节点指向了空,说明指针已经指向了链表尾,也就是没有找到
      if (posNode == NULL) {
        cout << "未找到指定信息" << endl;
        break;
      }
    }
    // 如果找到了,开始删除
    posNodeFront->next = posNode->next;
    free(posNode);
    posNode = NULL;
  }
}

删除节点

获取中间节点

  • 使用快慢指针的方法,慢指针一次走一格,快指针一次走两格,一旦快指针到达末尾,慢指针指向的就是中间位置
  • 代码示例:
// 获取中间节点 (只限于单数个节点状况)
struct Node* middleOddNode(Node* head)
{
  Node* fast = head; // 快指针
  Node* slow = head; // 慢指针
  while (fast != NULL && fast->next != NULL)
  {
    slow = slow->next;
    fast = fast->next;
    fast = fast->next;// fast指针每次走两格
  }
  return slow;
}

// 获取中间节点 (偶数的情况)
struct Node* middleEvenNode(Node* head) {
  Node* fast = head;
  Node* slow = head;
  while (fast != NULL && fast->next != NULL)
  {
    fast = fast->next;
    if (fast->next == NULL) // 这里随时可以判断他是否快指针到达了链表末尾
      break;
    slow = slow->next;
    fast = fast->next;
  }
  // 这里返回的是偶数情况下的中间偏前的那个节点,下一个中间节点直接.next就可以了
  return slow;
}
  • 主函数:
int main() {
  // 新建两个链表一个为奇数,一个为偶数
  struct Node* list1 = createList();
  struct Node* list2 = createList();
  for (int i = 0; i < 5; i++) {
    insertNodeByHead(list1, i);
  }
  // list1.data = {0,1,2,3,4}
  for (int i = 0; i < 12; i += 2) {
    insertNodeByHead(list2, i);
  }
  // list2.data = {0,2,4,6,8,10}
    struct Node* mid1 = middleNode(list1);
  cout << mid1->data << endl;
  struct Node* mid2 = middleNode(list2);
  cout << mid2->data << mid2->next->data << endl;
  return 0;
}

// 输出结果:
// 2
// 64

数据结构作业 1 详解

  • 题目描述:定义两个链表,求两个链表的交集,并且把两个链表合并

  • 代码详解:

1.链表的基础操作

#include<iostream>
using namespace std;

// 定义结构体
struct Node {
  int data;
  struct Node* next;
};

// 创建链表头
struct Node* createList() {
  struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
  headNode->next = NULL;
  return headNode;
}

// 创建节点 (和创建链表头基本相同)
struct Node* createNode(int data) {
  struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
  newNode->data = data;
  newNode->next = NULL;
  return newNode;
}

// 头插法
void insertNodeByHead(struct Node* headNode, int data) {
  struct Node* newNode = createNode(data);
  newNode->next = headNode->next;
  headNode->next = newNode;
}

// 打印节点
void printList(struct Node* headNode) {
  struct Node* pMove = headNode->next;
  while (pMove != NULL) {
    cout << pMove->data << "\t";
    pMove = pMove->next;
  }
  cout << endl;
  pMove = NULL;
}
  • 前三个函数是构建一个链表必不可少的三个函数,在使用时先定义一个链表头,然后使用头插法一个一个把节点插入 (创建节点的部分被在头插法里面被调用了)

2.把两个链表连接在一起

// 获取链表最后一个元素
struct Node* lastNode(struct Node* headNode) {
  struct Node* tempNode;
  while (true) {
    tempNode = headNode;
    headNode = headNode->next;
    if (headNode == NULL) {
      return tempNode;
    }
  }
}

// 合并两个链表函数 (尾插法合并两个链表)
void mergeNode(struct Node* firstNode, struct Node* secondNode) {
  struct Node* tempLastNode = lastNode(firstNode);
  tempLastNode->next = secondNode->next;
  free(secondNode);
  secondNode = NULL;
}
  • 连接链表思路:
  • 找到第一个链表的最后一个节点 (之前定义了一个lastNode函数)
  • 然后把第二个链表除了头节点的部分都接在后面
  • 然后释放第二个链表所指空间的内存,并且指针指向空

3.求两个链表的交集

// 链表长度函数
int nodeLength(struct Node* H)
{
  struct Node* p = H;
  int j = 0;
  while (p->next != NULL)
  {
    j++;
    p = p->next;
  }
  return j;
}

// 查找链表里面的值
int nodeData(struct Node* H,int mark)
{
  struct Node* p = H;
  int j = 0;
  while (j != mark)
  {
    j++;
    p = p->next;
  }
  return p->data;
}

// 获取两个链表的交集
void intersectionNode(struct Node* firstNode, struct Node* secondNode) {
  int list1Length = nodeLength(firstNode);
  int list2Length = nodeLength(secondNode);
  for (int i = 1; i <= list1Length; i++) {
    for (int j = 1; j <= list2Length; j++) {
      if (nodeData(firstNode, i) == nodeData(secondNode, j)) {
        cout << nodeData(firstNode, i) << "\t";
      }
    }
  }
}
  • 求两个链表的交集的思路:
  • 遍历一个链表里面的数据 (用到nodeLength作为循环终止条件),每个数据和另一个链表的所有数据进行比较 (用到nodeData来读取链表里的值),如果相等则输出,如果不等则接着进行循环

4.主函数部分

int main() {
  // 新建两个链表头部
  struct Node* list1 = createList();
  struct Node* list2 = createList();
  // 使用循环给链表添加节点并赋值
  for (int i = 0; i < 5; i++) {
    insertNodeByHead(list1, i);
  }
  // list1.data = {0,1,2,3,4}
  for (int i = 0; i < 10; i += 2) {
    insertNodeByHead(list2, i);
  }
  // list2.data = {0,2,4,6,8}

  // 输出两个链表
  cout << "链表一:";
  printList(list1);
  cout << endl;
  cout << "链表二:";
  printList(list2);
  cout << endl;

  // 求两个链表交集
  cout << "两个链表交集" << endl;
  intersectionNode(list1, list2);
  cout << endl;

  // 连接两个链表
  cout << "连接两个链表" << endl;
  mergeNode(list1, list2);
  // 因为连接完的两个链表是储存在 list1 里面的,所以输出 list1
  printList(list1);
  cout << endl;
  return 0;
}
  • 输出结果:
    输出结果
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值