1.free()
- free只能释放由malloc开辟的内存
- PNode pHead=(PNode)malloc(sizeof(Node));
pHead = NULL;
free(pHead)
可以释放空指针,If null, free call will do noting。
2.好像没什么好方法判断内存是否被释放?
3.时间复杂度
表示级数,常见的
O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)
没有什么O(1/2n^2),前面的系数都去掉。
for (int i = 0; i < n; i++)
{
for (int j = i; j < n; j++)
{
}
}
像这种,时间复杂度都是O(N^2)。
有平均时间复杂度和最坏时间复杂度,一般默认都是最坏时间复杂度。
线性表顺序存储结构插入一个元素平均移动次数:n/2
线性表顺序存储结构删除一个元素平均移动次数:(n-1)/2。
注意区别。
一、线性表
线性表的动态分配顺序存储结构
//线性表顺序存储结构(动态分配),感觉和静态分配没有毛线区别。
#include<stdio.h>
#define true 1
#define false 0
typedef int bool;
#define LISTSIZE 20
typedef struct SequenceList
{
int *data;
int length;//线性表已存储元素个数
int listsize;//线性表分配的空间大小
}SqList;
//初始化线性表
bool InitList(SqList *s)
{
s->data = (int *)malloc(sizeof(int)*LISTSIZE);
if (!s->data)
{
exit(-1);
}
s->length = 0;
s->listsize = LISTSIZE;
return 1;
}
//求线性表长度
int ListLength(SqList *s)
{
return s->length;
}
//判断线性表是否为空,是返回1,不是返回0
bool ListEmpty(SqList *s)
{
return s->length == 0;
}
//遍历线性表
void ListTraverse(SqList *s)
{
for (int i = 0; i < s->length; i++)
{
printf("%d ", s->data[i]);
}
printf("\n");
}
//插入操作,插入一个元素到线性表的第i个位置
//1.判断线性表是否已满
//2.判断i的位置是否合理
//3.从最后一个元素到当前元素后移
//4.当前元素插入
bool ListInsert(SqList *s,int i,int val)
{
if (s->length == s->listsize)
return false;//满了
if (i<1 || i>s->length + 1)
return false;//插入位置不合理
if (i <= s->length)
{
for (int k = s->length - 1; k >= i - 1; k--)
{
s->data[k + 1] = s->data[k];
}
}
s->data[i - 1] = val;
s->length++;
return true;
}
//删除第i个元素,用val存储
//1.判断线性表是否为空
//2.判断删除的位置是否合理
//3.存储删除的值
//从删除位置开始到结尾向前左移
bool ListDelete(SqList *s, int i, int *val)
{
if (s->data == 0)
return false;
if (i<1 || i>s->length)
return false;
*val = s->data[i - 1];
for (int k = i; k < s->length; k++)
{
s->data[k - 1] = s->data[k];
}
s->length--;
return true;
}
//清空线性表
void ListClear(SqList *s)
{
s->length = 0;
}
//销毁线性表,释放数据域,线性表尺寸和元素个数都置0
void ListDestroy(SqList *s)
{
free(s->data);
s->data = NULL;
s->length = 0;
s->listsize = 0;
}
//获取线性表第i个元素
bool ListGetElement(SqList *s,int i,int *val)
{
if (s->length == 0)
return false;
if (i > s->length || i < 1)
return false;
*val= s->data[i - 1];
return true;
}
//判断线性表中是否存在某个元素,如果存在返回第一个满足要求的位置,如果不存在,返回0
int ListLocateElement(SqList *s, int val)
{
for (int i = 0; i < s->length; i++)
{
if (val == s->data[i])
return i+1;
}
return 0;
}
//求线性表并集
void ListUnion(SqList *a, SqList *b)
{
int a_len = a->length;
int b_len = b->length;
for (int i = 0; i < b_len; i++)
{
if (!ListLocateElement(a, b->data[i]))
{
ListInsert(a, ++a_len, b->data[i]);
}
}
}
//冒泡排序
void ListBubble(SqList *s)
{
for (int i = 0; i < s->length-1; i++)
{
for (int j = 0; j < s->length - 1 - i; j++)
{
if (s->data[j] > s->data[j + 1])
{
int temp = s->data[j];
s->data[j] = s->data[j + 1];
s->data[j + 1] = temp;
}
}
}
}
//线性表倒序排列,简单,就是第一个和最后一个交换,第二个和倒数第二个交换
void ListReverse(SqList *s)
{
for (int i = 0; i < s->length / 2 ; i++)
{
int temp = s->data[i];
s->data[i] = s->data[s->length - i - 1];
s->data[s->length - i - 1] = temp;
}
}
int main(void)
{
SqList s;
//初始化线性表
InitList(&s);
printf("初始化线性表s后,判断线性表是否为空:%d\n", ListEmpty(&s));
printf("初始化线性表s后,求线性表的长度:%d\n", ListLength(&s));
//向线性表中插入几个元素
ListInsert(&s, 1, 1);
ListInsert(&s, 1, 2);
ListInsert(&s, 1, 3);
ListInsert(&s, 1, 4);
//遍历线性表
printf("插入元素之后,遍历线性表s:\n");
ListTraverse(&s);
//删除第二个元素
int val;
ListDelete(&s, 2, &val);
printf("删除s的第二个元素是:%d\n", val);
//遍历线性表
printf("删除s第二个元素之后,线性表为:\n");
ListTraverse(&s);
//线性表长度
printf("线性表s长度为:%d\n", ListLength(&s));
//再定义一个线性表b
SqList b;
InitList(&b);
ListInsert(&b, 1, 1);
ListInsert(&b, 1, 5);
ListInsert(&b, 1, 3);
ListInsert(&b, 1, 4);
ListInsert(&b, 5, 7);
//遍历线性表
printf("插入元素之后,遍历线性表b:\n");
ListTraverse(&b);
ListUnion(&s, &b);
//s和b的交集,遍历
printf("求s和b的交集:\n");
ListTraverse(&s);
//冒泡排序
printf("冒泡排序之后,线性表为:\n");
ListBubble(&s);
ListTraverse(&s);
//线性表转置
ListReverse(&s);
printf("转置线性表后,线性表为:\n");
ListTraverse(&s);
//清空列表s
ListClear(&s);
printf("清空列表s之后,判断线性表是否为空:%d\n", ListEmpty(&s));
return 0;
}
线性表的静态分配顺序存储结构
//线性表的静态分配顺序存储结构
//需要三个元素,1.数组长度,2.线性表长度,3.数据域
#include<stdio.h>
#define true 1
#define false 0
typedef int bool;
#define MAXSIZE 20//定义数组的长度
typedef struct SquenceList
{
int data[MAXSIZE];
int listlength;
}SqList;
//初始化线性表
void InitList(SqList *s)
{
s->listlength = 0;
}
//判断线性表是否为空,空返回1,非空返回0
bool ListEmpty(SqList *s)
{
return s->listlength == 0;
}
//在第i个位置插入一个元素
//1.判断线性表是否已满
//2.判断插入的位置是否正确
//3.将最后一个元素到插入点之后的元素依次后移
//4.将元素值插入到第i个位置
bool ListInsert(SqList *s, int i,int val)
{
if (s->listlength == MAXSIZE)
return false;
if (i<1 || i>s->listlength + 1)
return false;
if (i <= s->listlength)
{
for (int k = s->listlength - 1; k >= i-1; k--)
{
s->data[k + 1] = s->data[k];
}
}
s->data[i - 1] = val;
s->listlength++;
return true;
}
//删除线性表中第i个元素
//1.判断线性表是否为空
//2.判断删除位置是否合理
//3.取出要删除的元素
//4.从删除点到最后一个元素依次向前移动一个
bool ListDelete(SqList *s, int i, int *val)
{
if (s->listlength == 0)
return false;
if (i<1 || i>s->listlength)
return false;
*val = s->data[i - 1];
for (int k = i - 1; k < s->listlength-1; k++)
{
s->data[k] = s->data[k + 1];
}
s->listlength--;
return 1;
}
//遍历线性表
void ListTraverse(SqList *s)
{
for (int i = 0; i < s->listlength; i++)
{
printf("%d ", s->data[i]);
}
printf("\n");
}
//线性表长度
int ListLength(SqList *s)
{
return s->listlength;
}
//清空线性表,将线性表长度置为0,数组里面的元素也没法删
void ListClear(SqList *s)
{
s->listlength = 0;
}
//获取线性表第i个元素值,i指位置
bool ListGetElement(SqList *s, int i, int *val)
{
if (i<1 || i>s->listlength || s->listlength == 0)
return false;
*val = s->data[i - 1];
return true;
}
//查找线性表中是否存在元素val,如果存在返回第一个满足要求的位置,如果不存在返回0
int ListLocateElement(SqList *s, int val)
{
for (int i = 0; i < s->listlength; i++)
{
if (val == s->data[i])
return i + 1;
}
return 0;
}
//两个线性表并集,这里没有考虑线性表满问题
void ListUnion(SqList *a, SqList *b)
{
int a_len = a->listlength;
int b_len = b->listlength;
int val;
for (int i = 1; i <= b_len; i++)
{
ListGetElement(b, i, &val);
if (!ListLocateElement(a,val))
{
ListInsert(a, ++a_len, val);
}
}
}
int main(void)
{
SqList s;
//初始化线性表
InitList(&s);
printf("初始化线性表s后,判断线性表是否为空:%d\n", ListEmpty(&s));
printf("初始化线性表s后,求线性表的长度:%d\n", ListLength(&s));
//向线性表中插入几个元素
ListInsert(&s, 1, 1);
ListInsert(&s, 1, 2);
ListInsert(&s, 1, 3);
ListInsert(&s, 1, 4);
//遍历线性表
printf("插入元素之后,遍历线性表s:\n");
ListTraverse(&s);
//删除第二个元素
int val;
ListDelete(&s, 2, &val);
printf("删除s的第二个元素是:%d\n", val);
//遍历线性表
printf("删除s第二个元素之后,线性表为:\n");
ListTraverse(&s);
//线性表长度
printf("线性表s长度为:%d\n", ListLength(&s));
//再定义一个线性表b
SqList b;
InitList(&b);
ListInsert(&b, 1, 1);
ListInsert(&b, 1, 5);
ListInsert(&b, 1, 3);
ListInsert(&b, 1, 4);
ListInsert(&b, 5, 7);
//遍历线性表
printf("插入元素之后,遍历线性表b:\n");
ListTraverse(&b);
ListUnion(&s, &b);
//s和b的交集,遍历
printf("求s和b的交集:\n");
ListTraverse(&s);
//清空列表s
ListClear(&s);
printf("清空列表s之后,判断线性表是否为空:%d\n",ListEmpty(&s));
return 0;
}
单链表
//链表,带头结点
#include<stdio.h>
#include<malloc.h>
#define true 1
#define false 0
typedef int bool;
typedef struct Node
{
int data;
struct Node *next;
}Node;
typedef struct Node* PNode;
//初始化链表
bool InitList(PNode *pHead)
{
*pHead = (PNode)malloc(sizeof(Node));
if (!(*pHead))//分配空间失败
{
return false;
}
(*pHead)->next = NULL;
return true;
}
//第i个位置插入一个链表节点
bool ListInsert(PNode pHead, int i, int val)
{
int j = 0;
PNode p = pHead;
PNode q = (PNode)malloc(sizeof(Node));
while (p&&j < i - 1)
{
p = p->next;
j++;
}
if (!p || j > i-1)
{
return false;
}
q->data = val;
q->next = p->next;
p->next = q;
return true;
}
//删除第i个位置的节点
bool ListDelete(PNode pHead, int i, int* val)
{
int j = 0;
PNode p = pHead;
while (p->next&&j < i - 1)//注意while(p->pNext!=NULL&&i<pos-1)与while(p!=NULL&&i<pos-1)的差别,比如链表长度为6,当数大于等于8时有区别
{
p = p->next;
j++;
}
if (!p->next || j > i-1)
{
return false;
}
PNode q = p->next;
*val = q->data;
p->next = q->next;
free(q);
return true;
}
//判断链表是否为空
bool ListEmpty(PNode pHead)
{
if (pHead->next)
{
return false;
}
else
{
return true;
}
}
//求链表长度
int ListLength(PNode pHead)
{
int i = 0;
PNode p = pHead;
while (p->next)
{
i++;
p = p->next;
}
return i;
}
//遍历链表
void ListTraverse(PNode pHead)
{
PNode p = pHead->next;
while (p)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}
//获取链表第i个元素的值
bool ListGetElement(PNode pHead, int i, int *val)//val存储返回值
{
int j = 0;
PNode p = pHead;
while (p->next&&j<i-1)
{
p = p->next;
j++;
}
if (!p->next || j > i - 1)
{
return false;
}
*val = p->next->data;
return true;
}
/* 初始条件:顺序线性表L已存在 */
/* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */
/* 若这样的数据元素不存在,则返回值为0 */
int ListLocateElement(PNode pHead, int val)
{
PNode p = pHead->next;
int i = 0;
while (p)
{
i++;
if (p->data == val)
return i;
p = p->next;
}
return 0;
}
//链表选择排序
void ListSelectSort(PNode pHead)
{
PNode p = pHead->next;
PNode q = p->next;
int length = ListLength(pHead);
for (int i = 0; i < length-1; i++)
{
for (int j = i + 1; j < length; j++)
{
if (p->data > q->data)
{
int temp = p->data;
p->data = q->data;
q->data = temp;
}
q = q->next;
}
p = p->next;
q = p->next;
}
}
//将链表置为空链表,除了头结点外,其他都释放
void ListClear(PNode pHead)
{
PNode p = pHead->next;
PNode q;
while (p)
{
q = p->next;
free(p);
p = q;
}
pHead->next = NULL;
}
//销毁链表,头结点也不存在
void ListDestroy(PNode *pHead)
{
PNode p;
while (*pHead)
{
printf("%d\n", 1);
p = (*pHead)->next;
printf("%d\n", 2);
free(*pHead);
printf("%d\n", 3);
*pHead = p;
printf("%d\n", 4);
}
}
//创建链表,从头插入
void CreateListHead(PNode pHead)
{
PNode p = pHead;
int num, data;
printf("清输入要创建的节点个数\n");
scanf("%d", &num);
for (int i = 0; i < num; i++)
{
scanf("%d", &data);
PNode q = (PNode)malloc(sizeof(Node));
q->data = data;
q -> next = p->next;
p->next = q;
}
}
//创建链表,从尾插入
void CreateListTail(PNode pHead)
{
PNode p = pHead;
int num, data;
printf("清输入要创建的节点个数\n");
scanf("%d", &num);
for (int i = 0; i < num; i++)
{
scanf("%d", &data);
PNode q = (PNode)malloc(sizeof(Node));
q->data = data;
p->next = q;
p = q;
}
p->next = NULL;
}
//反转链表
//思路:每次都将原第一个结点之后的那个结点放在新的表头后面。
//比如1, 2, 3, 4, 5
//第一次:把第一个结点1后边的结点2放到新表头后面,变成2, 1, 3, 4, 5
//第二次:把第一个结点1后边的结点3放到新表头后面,变成3, 2, 1, 4, 5
//……
//直到: 第一个结点1,后边没有结点为止。
PNode ListReverse(PNode pHead)
{
//链表只有头节点,或者只有一个节点
if (pHead->next == NULL || pHead->next->next == NULL)
{
return pHead;
}
PNode tail = pHead->next;//第一个节点为尾
PNode current = tail->next;//current为当前需要调换到前面的节点
while (current)
{
tail->next = current->next;
current->next = pHead->next;
pHead->next = current;
current = tail->next;
}
return pHead;
}
//查找链表倒数第K个节点
//方法一:需遍历两次链表;先求链表的长度len,然后遍历len-k的距离即可查找到单链表的倒数第k个节点
//方法二:遍历一次链表,时间复杂度为O(n);设置两个指针p1、p2,让p2先走k-1步,然后p1、p2再同时走,
// 当p2走到链表尾时,p1所指位置就是所要找的节点
PNode ListSearchReverseKthNode(PNode pHead, int k)
{
if (pHead->next == NULL || k <= 0)
{
return NULL;
}
PNode p1 = pHead->next;
PNode p2 = pHead->next;
while (p2&&--k)
{
p2 = p2->next;
}
while (p2->next)
{
p2 = p2->next;
p1 = p1->next;
}
return p1;
}
int main(void)
{
PNode pHead;
//初始化链表
InitList(&pHead);
//初始化之后判断链表是否为空,求链表长度
printf("初始化之后链表是否为空:%d\n", ListEmpty(pHead));
printf("初始化之后链表的长度:%d\n", ListLength(pHead));
//创建链表,从头插入元素
CreateListHead(pHead);
//创建链表,头插法
ListTraverse(pHead);
创建链表,尾插法,头插法和尾插法二选一,一起用会影响。
//CreateListTail(pHead);
//ListTraverse(pHead);
//对链表进行排序
ListSelectSort(pHead);
//输出排序之后的链表内容
printf("链表排序:\n");
ListTraverse(pHead);
//获取链表第5个元素值
int val;
ListGetElement(pHead, 5, &val);
printf("第五个元素值为:%d\n", val);
//删除链表第2个元素
ListDelete(pHead, 2, &val);
printf("删除第2个元素值后,链表的值为:\n");
ListTraverse(pHead);
//链表第二个位置插入100
ListInsert(pHead, 2, 100);
printf("链表第2个元素改为100\n");
ListTraverse(pHead);
printf("pHead内容:%x\n", pHead);
//链表反转
ListReverse(pHead);
printf("链表反转:\n");
ListTraverse(pHead);
//查找数组中第一个元素3的位置
printf("第一个元素3的位置为:%d\n", ListLocateElement(pHead, 3));
//查找链表倒数第3个元素
PNode p = ListSearchReverseKthNode(pHead, 3);
printf("链表倒数第3个元素:%d\n", p->data);
//清空链表
ListClear(pHead);
printf("清空链表之后,链表是否为空:%d\n", ListEmpty(pHead));
printf("pHead内容:%x\n", pHead);
printf("链表长度:%d\n", ListLength(pHead));
销毁链表
ListDestroy(&pHead);
printf("%d\n", 5);
//销毁链表之后,就能不在判断链表是否为空了。怎么确认链表是否销毁了
/*printf("销毁链表之后,链表是否为空:%d\n", ListEmpty(pHead));
printf("pHead内容:%p\n", pHead);
printf("链表长度:%d\n", ListLength(pHead));*/
return 0;
}
循环链表
带头指针的循环链表示意图
最后一个节点指向的是头结点,不是第一个节点。
带尾指针的循环链表示意图
双向链表
一般都构造双向循环链表,双向循环链表一般都带头结点。
带头结点的双向循环链表示意图:
为空时:
循环链表主要优点:从表中任一结点出发都能遍历整个链表
双循环链表主要优点:方便找到前驱,任意节点都能遍历,方便在链表尾部操作。
//实现一个带头节点和尾指针的单循环链表。定义尾指针而不定义头指针是为了方便两个链表连接
#include<stdio.h>
#define true 1
#define false 0
typedef int bool;
typedef struct Node
{
int data;
struct Node *next;
}Node ,*PNode;
//初始化链表
bool InitList(PNode *pTail)
{
*pTail = (PNode)malloc(sizeof(Node));
if (!*pTail)
{
exit(-1);
}
(*pTail)->next = *pTail;
return true;
}
//求长度
int ListLength(PNode pTail)
{
int i = 0;
PNode p = pTail->next;
while (p != pTail)
{
i++;
p = p->next;
}
return i;
}
//第i个位置插入一个元素,尾节点有点烦人,插入和删除的时候,都要注意改变pTail的值
//所以这里要传入*pTail.
bool ListInsert(PNode *pTail, int i, int val)
{
PNode p = (*pTail)->next;//p指向头结点
int j = 0;
/*while (p&&j < i-1)
{
p = p->next;
j++;
}
if (!p || j > i - 1)
{
return false;
}*/
if (i <= 0 || i > ListLength(*pTail) + 1)
return false;
while (j < i - 1)
{
p = p->next;
j++;
}
PNode q = (PNode)malloc(sizeof(Node));
q->data = val;
q->next = p->next;
p->next = q;
if (p == (*pTail))
(*pTail) = q;
return true;
}
//遍历
void ListTraverse(PNode pTail)
{
PNode p = pTail->next->next;//p指向第一个元素
while (p!=pTail->next)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//删除
bool ListDelete(PNode *pTail, int i, int *val)
{
PNode p = (*pTail)->next; // p指向头结点
int j = 0;
//while (p&&j < i - 1)//不要这样判断很烦,容易出错
//{
// p = p->next;
// j++;
//}
//if (!p || j > i - 1)
//{
// return false;
//}
if (i <= 0 || i > ListLength(*pTail))
{
printf("删除元素位置不正确\n");
return false;
}
while (j < i - 1)
{
p = p->next;
j++;
}
PNode q = p->next;
*val = q->data;
p->next = q->next;
if (q == *pTail)//删除的是表尾元素
*pTail = p;
free(q);
return true;
}
int main(void)
{
PNode pTail;
//初始化链表
InitList(&pTail);
//插入元素
ListInsert(&pTail, 1, 1);
ListInsert(&pTail, 1, 2);
ListInsert(&pTail, 1, 3);
ListInsert(&pTail, 1, 4);
ListInsert(&pTail, 1, 5);
//遍历链表
ListTraverse(pTail);
//删除第5个元素
int val;
ListDelete(&pTail,5, &val);
printf("删除的元素为:%d\n", val);
//遍历链表
ListTraverse(pTail);
return 0;
}