/*
删除:
1.删除线性表中元素为X的值
2.将顺序表中所有元素偶数元素放到奇数元素之前(14)
3.去掉有序线性表中相同元素(16)
反转交换:
4. 反转线性表
5. 循环左移线性表n个元素
合并:
6. 合并两个升序线性表(03)
7. 求两个线性表的集合元素的并集。
思维拓展(要求最佳性能)
8. 找出顺序表中出现次数大于一半的元素
9. 找出有共同后缀的两条单链表中第一个公共结点
10. 判断链表是否有环
*/
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef int ElemType; // 给int类型起了别名ElemType
struct SeqList{ // 顺序表
ElemType* data; // 数据域
int length; // 表长
int size; // 数据域能保存的大小
SeqList(int size) { // 构造函数,数据初始化
data = (ElemType*)malloc(size*sizeof(ElemType)); // 动态分配空间
length = 0;
this->size = size;
}
};
struct LNode { // 单链表节点
ElemType data; // 数据域
LNode* next; // 指针域,指向下一个元素
LNode(ElemType d) {
data = d;
next = NULL;
}
};
typedef LNode* LinkList; // 将节点指针重命名
LinkList createLinkList(ElemType arr[], int n) {
// 将数组arr中n个元素构成单链表
LinkList L = (LNode*)malloc(sizeof(LNode)); // 头节点
L->next = NULL;
LinkList r = L; // 位指针
for(int i=0; i<n; ++i) {
LNode* p = (LNode*)malloc(sizeof(LNode)); // 开辟一个节点
p->data = arr[i]; // 将数组中的元素赋值给节点p
p->next = NULL;
r->next = p; // 用尾插法将节点p插入L
r = p;
}
return L;
}
SeqList createSeqList(ElemType* arr, int n) {
SeqList L(2*n);
for(int i=0; i<n; i++) {
L.data[i] = arr[i];
}
L.length = n;
return L;
}
void show(LinkList L) {
// 输出单链表L中所有元素
LNode* p = L->next;
while(p!=NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
void show(SeqList L) {
// 输出顺序表L中所有元素
for(int i=0; i<L.length; ++i) {
printf("%d ", L.data[i]);
}
printf("\n");
}
//1.删除线性表中元素为X的值
void deleteX(LinkList L, ElemType x) {
// 删除单链表L中元素为X的节点
LNode* p = L; // 扫描指针
while(p->next != NULL) {
if(p->next->data == x) { // 找到了x元素
LNode* q = p->next; // 先指向x元素
p->next = q->next; // 删除x元素
free(q); // 释放x元素空间
} else { // 扫描下一个
p = p->next;
}
}
}
void deleteX(SeqList& L, ElemType x) {
// 删除顺序表中的元素x
int i = 0, j = 0; // i是扫描指针,将需要保留的元素放在[0,j)区间里面
for(; i< L.length; ++i) {
if(L.data[i] != x) { // 需要保留的元素前移动到j上
L.data[j++] = L.data[i];
}
}
L.length = j;
}
void preOdd(SeqList& L) {
// 2.将顺序表L中所有元素偶数元素放到奇数元素之前(14)
int i = 0, j = 0; // i是扫描指针,将需要保留的元素放在[0,j)区间里面
for(; i< L.length; ++i) {
if(L.data[i]%2==0) { // 将偶数元素前移动到j上
swap(L.data[j], L.data[i]); // 交换元素i和元素就
j++;
}
}
}
// 3. 去掉有序线性表中相同元素
// 相同元素在有序表中必然是相邻,在删除x中是判断每个元素否则(不)等于x,这里改为判断相邻元素释放相等。
// 留给大家实现
void deleteSame(SeqList& L) {
// 删除升序顺序表L中相同的元素
int j = 0; // 将保留元素放在[0, j]
for(int i=1; i<L.length; i++) {
if(L.data[j] != L.data[i]) { // 发现相邻不一样的元素
L.data[++j] = L.data[i];
}
}
L.length = j+1;
}
void deleteSame(LinkList L) {
// 删除升序顺序表L中相同的元素
LNode* p = L->next; // 扫描指针
while(p && p->next) {
if(p->next->data == p->data) { // 要删除的结点
LNode* q = p->next;
p->next = q->next; // 删除了q结点(链表上)
free(q); // 释放其空间
} else { // 否则p往后检查
p = p->next;
}
}
}
void reverse(SeqList& L) {
for(int i=0, j=L.length-1; i<j; i++,j--) { // i从前往后,j从后往前扫描,直到相遇
swap(L.data[i], L.data[j]); // 交换第i个和第j个(倒数第i个)元素
}
}
void reverse(LinkList L) {
// 翻转单链表
LNode* p = L->next; // 扫描指针
L->next = NULL; // 取出头节点
while(p != NULL) {
LNode* q = p->next; // 保存p的后继节点
p->next = L->next; // 用头插法插入L链表
L->next = p;
p = q; // p指针后移
}
}
void reverse2(LinkList L) {
//反转顺序表L ,和reverse功能一样,借助了三个指针分别来指向当前扫描节点和其前驱、后继
LNode* p = L->next;
LNode* r = NULL; // 在原链表中p的前驱
LNode* q; // 在原链表中p的后继
while(p) {
q = p->next; // 先保存p的后继,防止断链表
p->next = r; // 反转链表
r = p; // 往后移动
p = q;
}
L->next = r; // 最后一个结点成为第一个结点
}
void reverse(SeqList& L, int l, int h) {
// 翻转L[l, h)中的元素
for(int i=l, j=h-1; i<j; i++,j--) { // i从前往后,j从后往前扫描,直到相遇
swap(L.data[i], L.data[j]); // 交换第i个和第j个(倒数第i个)元素
}
}
void reLeftShift(SeqList L, int n) {
n = n % L.length; // 循环左移length个元素就和原来一样了
reverse(L, 0, L.length); // 全部翻转
reverse(L, 0, n); // 翻转前n个部分
reverse(L, n, L.length); // 翻转后面元素
}
int getLength(LinkList L) {
// 得到单链表L的长度
int len = 0;
LNode* p = L->next;
while(p) {
p = p->next;
len++;
}
return len;
}
void ReLeftShift(LinkList L, int n) {
// 循环左移n个元素
int len = getLength(L); // 单链表长度
n = n % len; // 因为循环左移length个元素就相对于没移动
LNode* p = L; // 第n个结点的前驱
for(int i=0; i<len-n; i++) p = p->next;
LNode* q = p; // 尾指针
while(q->next) q = q->next;
q->next = L->next; // 这三句话的顺序建议在纸上画一下,保证不断链接
L->next = p->next;
p->next = NULL;
}
// 6. 合并两个升序线性表(03)
SeqList merge(SeqList L1, SeqList L2) {
// 归并两个升序线性表L1,和L2并放回结果
SeqList L(L1.length+L2.length);
int i = 0, j = 0; // 扫描L1和L2的指针
int k = 0; // 已经归并好的元素个数
while(i<L1.length || j<L2.length) { // L1或者L2中还有元素没有归并
if(j>=L2.length || i<L1.length && L1.data[i] <= L2.data[j]) { // 拿取L1首元素情况:L2已经拿完或者L1没有拿完并且L1首元素比L2首元素小
L.data[k++] = L1.data[i++];
} else {
L.data[k++] = L2.data[j++];
}
}
L.length = k; // 修改L的长度
return L;
}
void merge(LinkList L1, LinkList L2) {
// 归并两个升序单链表L1,和L2将结果保存在L1中
LNode* p = L1->next, *q = L2->next;
LNode* r = L1; // 合并以后的链表尾部
while(p&&q) {
if(p->data <= q->data) { // 比较p和q的值,将较小的尾插到r后面
r->next = p; // 尾插法插入
r = p; // 修改为指针
p = p->next;
} else {
r->next = q;
r = q;
q = q->next;
}
}
if(p) r->next = p; // 将剩余的元素全部插入r后面
else r->next = q;
}
// 7. 求两个线性表的集合元素的并集。
void Union(SeqList& L1, SeqList L2) {
// 求顺序表L1和L2的并集,放入L1中
for(int i=0; i<L2.length; i++) { // 遍历L2中的每一个元素
bool ok = false; // 标记L2的第i个元素是否在L1中
for(int j=0; j<L1.length; j++) { // 查找L1的第i个元素是否在L2中
if(L1.data[j]==L2.data[i]) { // 找到
ok = true;
break;
}
}
if(!ok) { // 如果不在L1中,即是并集中的元素
L1.data[L1.length++] = L2.data[i];
}
}
}
int Union(LinkList A, LinkList B){
//遍历A的每一个元素,若它存在于B中,则从B中删除,反之不处理
LNode *p = A;
for(; p->next!=NULL; p = p->next){ // 遍历A
for(LNode *q = B; q->next!=NULL; q = q->next){ // 遍历B
if(p->next->data==q->next->data){ // A中元素在B中
LNode* d = q->next; // 将B中的元素删除
q->next = d->next;
free(d);
}
}
}
//最后把改变过的B加到A末尾
p->next=B->next;
}
// 思考交集、差集如何求
// 8. 找出顺序表中出现次数大于一半的元素
ElemType major(SeqList L) {
// 找出线性表L中出现次数大于一半的元素,找到返回元素,没有返回-1
ElemType m = L.data[0]; // m为出现次数大于一半的元素,初始时默认为第一个元素
int cnt = 1; // cnt为最多元素出现次数减去其他元素出现次数
for(int i=1; i<L.length; i++) { // 扫描表中剩余元素
if(L.data[i] != m) { // 如果不等于m,cnt减一
cnt--;
if(cnt==0) { // 如果为0了,则m不可能是出现次数大于一半的元素
m = L.data[i]; // 更新m值
cnt = 1; // 更新cnt
}
} else { // 如果和m相等,则出现次数加一
cnt++;
}
}
cnt = 0; // 统计m在L中出现次数
for(int i=0; i<L.length; i++) {
if(L.data[i]==m) cnt++;
}
if(cnt>=(L.length+1)/2) return m; // 超过一半则返回m
else return -1; // 否则返回-1
}
// 9. 找出有共同后缀的两条单链表中第一个公共结点
LNode* searchFirstCommon(LinkList L1, LinkList L2) {
// 找出有共同后缀的两条单链表L1,L2中第一个公共结点并返回
int len1 = getLength(L1), len2=getLength(L2); // 计算两个链表的表长
LNode* p = L1->next, *q = L2->next; // L1和L2的扫描指针
while(len1!=len2) { // 当前两个表长不一样
if(len1>len2) {p=p->next; len1--;} // 将长度那个往后移动
else {q=q->next; len2--;}
}
while(p!=NULL && p!=q) { // 扫描到最后前没有找到公共结点
p=p->next; q = q->next; // 两个表同时往后扫描
}
return p; // 返回公共结点
}
// 10. 判断链表是否有环
bool isExistLoop(LinkList L) {
// 判断带头结点链表中是否存在环
LNode *fast,*low; // 快慢指针扫描
fast=low=L;
while(fast!=NULL&&low!=NULL&&fast->next!=NULL) {
fast=fast->next->next; // 快指针一次走两步
low=low->next; // 慢指针一次走一步
if(fast==low) // 如果碰头则存在环
return true;
}
return false; // 否则不存在
}
int main() {
int arr1[] = {1,2,2,7,5};
int arr2[] = {4,5};
LinkList L1 = createLinkList(arr1, sizeof(arr1)/sizeof(arr1[0]));
LinkList L2 = createLinkList(arr2, sizeof(arr2)/sizeof(arr2[0]));
L1->next->next->next->next->next = L1->next->next;
bool isLoop = isExistLoop(L1);
printf("%d", isLoop);
return 0;
}