消除链表中重复的元素
↓_p
↓_head ↓_tail
[数据1|P_next] [数据2|P_next] [数据3|P_next] [数据4|P_next] Null
↑_P_node1 ↓___↑ ↓___↑ ↓___↑ ↓___↑
↓_p
↓_head ↓_q ↓_tail
[数据1|P_next] [数据1|P_next] [数据3|P_next] [数据4|P_next] Null
↑_P_node1 ↓___↑ ↓___↑ ↓___↑ ↓___↑
↓_p
↓_head ↓_tail
[数据1|P_next] [数据3|P_next] [数据4|P_next] Null
↑_P_node1 ↓________________↑ ↓___↑ ↓___↑
如何标记某个数字已经出现
辅助数组:
如果flag[i] = flag[j]表示有重复
下标表示要检索的内容,数组中的元素用来标识状态
flag[] = 0;数组中的每个元素都等于0,0代表没出现过,若出现过就改为1
flag[4] = 0; 代表当前4没有出现过
flag[3] = 1; 代表当前3有出现过
#include "delete_repeat.h"
#include <iostream>
using namespace std;
int main(void)
{
struct Node* p_head1 = NULL;
create_list(1);
create_list(3);
create_list(5);
create_list(7);
// 把上面链表的头指针指向的地址赋值给新指针
p_head1 = p_head;
print_linklist(p_head1);
p_head = NULL;
struct Node* p_head2 = NULL;
create_list(1);
create_list(4);
create_list(6);
create_list(8);
create_list(9);
create_list(10);
// 把上面链表的头指针指向的地址赋值给新指针
p_head2 = p_head;
print_linklist(p_head2);
p_head = NULL;
merge_linlist(p_head1, p_head2);
// 打印合并后的值
print_linklist(p_head);
cout << "删除合并后重复内容后:" << endl;
delete_repeat(p_head);
print_linklist(p_head);
cout << "-------------------------" << endl;
p_head = NULL;
struct Node* p_head3 = NULL;
create_list(1);
create_list(6);
create_list(6);
create_list(2);
create_list(1);
create_list(2);
create_list(8);
p_head3 = p_head;
print_linklist(p_head3);
delete_repeat(p_head3);
cout << "删除重复内容后:" << endl;
print_linklist(p_head3);
return 0;
}
delete_repeat.cpp
#include <iostream>
#include "delete_repeat.h"
using namespace std;
// 头指针指向整个链表的第一个节点的地址,插入数据时,不可移动
struct Node* p_head = NULL;
// 不断追加数据时需要尾指针,始终指向链表的末尾,插入数据时,可以移动
struct Node* p_tail = NULL;
// 创建节点
void create_list(unsigned int elem)
{
// 节点的数据占用多大的空间就开辟多大的内存空间,返回void* 类型,需要强制转换为节点的类型
struct Node* p_node = (struct Node *)malloc(sizeof(struct Node));// 节点地址被赋值给指针P
// 初始化数据域
p_node->elem = elem;
// 初始化指针域,此时并没有串联节点,指针指向空
p_node->p_next = NULL;
// 若一个节点都没有放到链表中
if(p_head == NULL)
p_head = p_node;// 插入数据时,不可移动
else
// 第二次调用时:P_tail = p_node1 = 第一个节点的地址,相当于第一个节点的指针域指向第二个节点的节点地址(p_node1->p_next = p)
p_tail->p_next = p_node;
// 第一次赋值的是第一个节点地址(只有一个元素:头=尾),第二次赋值的是第二个节点地址
p_tail = p_node;
/*
第一次调用create_list
↓_head = P_node1 ↓_tail = P_node1
[数据1|指针] Null
↑ ↓___↑
P_node1 P_next
第二次调用create_list
↓_head ↓_tail = P_node1
[数据1|指针] [数据2|指针] Null
↑ ↓___↑ ↓___↑
P_node1 P_next = P_node2
...........
*/
}
// 在链表中插入节点:1.插入的位置,插入的值
void insert_node(int pos, unsigned int elem)
{
// 前节点
struct Node *pre;
// 初始化前节点,从头开始
pre = p_head;
// 循环次数
int i = 0;
struct Node* p_new = (struct Node *)malloc(sizeof(struct Node));
// 如果插入的位置是头节点 ,头节点没有前节点,
if(pos == 0)
{
p_new->elem = elem;
// 新节点的指针域指向头节点(原来头节点的地址)
p_new->p_next = p_head;
// 更新“让新节点作为头节点
p_head = p_new;
}
else
{
// 遍历链表,从头开始找,直到找到指定位置的前节点
// 如找到位置3,则循环两次,循环次数 = 位置 -1
while(i < pos - 1)
{
pre = pre->p_next;
i++;
}// 1.跳出循序找到前节点
p_new->elem = elem;
// 2.前节点的指针域赋值(pos后节点的地址)给新节点的指针域
p_new->p_next = pre->p_next;
// 3.让pos前节点的指针域指向新节点的地址
pre->p_next = p_new;
// 链表末尾
if(p_new->p_next == NULL)
// 更新
p_tail = p_new;
}
}
void delete_node(int pos)
{
// 前节点,要删除的节点
struct Node *pre, *p_delet;
pre = p_head;
int i = 0;
// 删除头节点
if(pos == 0)
{
// 更新
p_head = p_head->p_next;
// 释放头节点
free(pre);
}
else
{
while(i < pos - 1)
{
pre = pre->p_next;
i++;
}// 1.找到前节点
// 2.前节点的指针域指向的是要删除的节点的地址
p_delet = pre->p_next;
// 3.让pos前节点的指针域指向要删除的节点地址(pos的后节点),相当于删除
pre->p_next = p_delet->p_next;
// 删除尾节点
if(p_delet->p_next == NULL)
p_tail = pre;// 更新
// 释放当前要删除节点所占用的内存空间
free(p_delet);
}
}
// 显示链表数据域
void print_linklist(struct Node* linlist_head)
{
struct Node *p;
// p不为空的话就一直更新p的位置
for(p = linlist_head; p; p = p->p_next)
cout << p->elem << " ";
cout << endl;
}
int search(unsigned int elem)
{
struct Node *p;
for(p = p_head; p; p = p->p_next)
if(p->elem == elem)
return 1;
return 0;
}
void merge_linlist(struct Node* p, struct Node* q)
{
// p和q只要有一个为空就退出循环
while(p && q)
{
if(p->elem <= q->elem)
{
// 若一个节点都没有放到链表中
if(p_head == NULL)
// 更新头
p_head = p;// 第一次之后都不更改,不可移动
else
// 第二次赋值时:P_tail = p_node1,相当于p_node1->p_next = p_2
p_tail->p_next = p;
// 第一次赋值的是第一个节点地址(只有一个元素:头=尾),第二次赋值的是第二个节点地址
p_tail = p;
// 更新p,指向下一个节点
p = p->p_next;
}
else
{
if(p_head == NULL)
p_head = q;
else
p_tail->p_next = q;
// 更新尾指针
p_tail = q;
// 更新q
q = q->p_next;
}
}
// 还剩9和10没有添加到节点数据域,若p为空则取q,不为空则取p
p_tail->p_next = p?p:q;// 指向9,9又关联10
}
// 入参:删除哪一个链表中的重复的节点
void delete_repeat(struct Node* head)
{
int flag[11] = {0,0,0,0,0,0,0,0,0,0};
// 定义一个指针指向数组的节点
struct Node* p = head;
struct Node* p_delete = NULL;
// 肯定有第一个节点,第一个节点肯定出现过,不然为空
flag[p->elem] = 1;
// 如果下一个节点存在
while(p->p_next != NULL)
{
// 如果下一个节点没有被标记
if(flag[p->p_next->elem] == 0)
{
// 标记
flag[p->p_next->elem] = 1;
// 更新到下一个节点
p = p->p_next;
}
else// 如果有重复元素:flag[6] == 1
{
// 把前操作节点(p_next)的地址赋值给上上节点的p_next,就相当于删除上一个节点
p_delete = p->p_next;
p->p_next = p_delete->p_next;
free(p_delete);
}
}
}
delete_repeat.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <iostream>
// 声明一个外部变量,在其他文件可见可赋值
extern struct Node* p_head;
extern struct Node* p_tail;
struct Node
{
// 数据域
unsigned int elem;
// 指针域
struct Node* p_next;
};
void create_list(unsigned int elem);
void insert_node(int pos, unsigned int elem);
void delete_node(int pos);
void print_linklist(struct Node* linlist_head);
int search(unsigned int elem);
void merge_linlist(struct Node* p, struct Node* q);
void delete_repeat(struct Node* head);
#endif