数据结构:栈、队列、排序、 算法的复杂度

一、栈和队列
栈和队列都是对结点操作位置有要求的特殊线性表
栈(先进后出)----->子弹入膛
队列(先进先出)----->食堂排队
二、栈(数据结构)
1.概念
    线性表的插入(压栈)和删除(出栈)都只能在同一个端点进行,不能在其他位置,这样的结构称之为栈
2.分类
    顺序栈、链式栈(带头结点的单向不循环链表)
3.特性
    后进先出,一端是完全封死的,只有另外一端是用来控制和插入的,所以说,最先进来的结点肯定是最后出去的
4.链式栈(stack)
    其实就是一个头插头删或者尾插尾删的链表
三、链式栈的设计和创建

1.设计:
typedef int SElemType_t;
//数据结点
struct node
{
    SElemType_t data;
    struct node *next;
};
//链式栈的管理结构体(头结点)
struct list_stack
{
        struct node *stack;//保存首结点的地址
        int size;//栈结构体中元素的个数(结点个数)
};
struct list_stack *managerStack;
//2.初始化栈
bool init_stack()
{
    //1)申请栈管理结构体的内存空间
    managerStack=malloc(sizeof(struct list_stack));
    if(managerStack==NULL)
    {
        printf("malloc managerStack error\n");
        return false;
    }
    //2)初始化
    managerStack->size=0;
    managerStack->stack=NULL;
    return true;
}
//3.入栈(压栈)
bool push(SElemType_t inputData)
{
    //1、申请栈元素的结点的内存空间
    struct node *newNode=malloc(sizeof(struct node));
    if(newNode==NULL)
    {
        printf("malloc newNode error\n");
        return false;
    }
    //2、初始化
    newNode->data=inputData;
    newNode->next=NULL;
    //3、插入
    if(managerStack->stack==NULL)//从无到有
    {
        managerStack->stack=newNode;
    }
    else//(由少到多)(头插)
    {
        newNode->next=managerStack->stack;
        //更新首结点
        managerStack->stack=newNode;
    }
    //栈元素+1
    managerStack->size++;
    return true;
}
bool isEmpty()
{
    return managerStack->size==0;
}
//出栈--删除(头删)
bool pop(SElemType_t *outData)
{
    //1、先判断当前有没有栈元素
    if(isEmpty())
        return false;
    //2、先获取出栈的数据
    *outData=managerStack->stack->data;
    //先定义一个临时的指针存储当前删除结点的地址
    struct node*delNode=managerStack->stack;
    //更新首结点
    managerStack->stack=delNode->next;
    //释放
    free(delNode);
    //size--
    managerStack->size--;
    return true;
}
//销毁栈
void destory_stack()
{
    if(managerStack==NULL)
        return;
    //1.遍历所有的结点,每个点都删除
    struct node *p=managerStack->stack;
    struct node *pnext=NULL;
    while(p)
    {
        pnext=p->next;
        free(p);
        p=pnext;
    }
    //2.释放头结点(栈管理结构体)
    free(managerStack);
    managerStack=NULL;
}

demo.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int SElemType_t;

struct node{
	SElemType_t data;
	struct node *next;
};

struct list_stack
{
	struct node *stack;
	int size;
};

struct list_stack *managerStack;

bool init_stack()
{
	managerStack = malloc(sizeof(struct list_stack));
	if(managerStack == NULL)
	{
		printf("malloc managerStack error\n");
		return false;
	}
	
	managerStack->size = 0;
	managerStack->stack = NULL;
	
	return true;
}

bool push(SElemType_t inputData)
{
	struct node *newNode = malloc(sizeof(struct node));
	if(newNode==NULL)
	{
		printf("malloc newNode error\n");
		return false;
	}
	
	newNode->data = inputData;
	newNode->next = NULL;
	
	if(managerStack->stack == NULL)
	{
		managerStack->stack = newNode;
	}
	else
	{
		newNode->next = managerStack->stack;
		managerStack->stack = newNode;
	}
	
	managerStack->size++;
	
	return true;
}

bool isEmpty()
{
	return managerStack->size==0;
}
//出栈--删除(头删)
bool pop(SElemType_t *outData)
{
	//1、先判断当前有没有栈元素
	if(isEmpty())
		return false;
	//2、先获取出栈的数据
	*outData=managerStack->stack->data;
	//先定义一个临时的指针存储当前删除结点的地址
	struct node*delNode=managerStack->stack;
	//更新首结点
	managerStack->stack=delNode->next;
	//释放
	free(delNode);
	//size--
	managerStack->size--;
	return true;
}
//销毁栈
void destory_stack()
{
	if(managerStack==NULL)
		return;
	//1.遍历所有的结点,每个点都删除
	struct node *p=managerStack->stack;
	struct node *pnext=NULL;
	while(p)
	{
		pnext=p->next;
		free(p);
		p=pnext;
	}
	//2.释放头结点(栈管理结构体)
	free(managerStack);
	managerStack=NULL;
}
void main()
{
	int data;
	init_stack();
	push(10);
	push(20);
	push(30);
	push(40);
	while(pop(&data))
	{
		printf("%d\n",data);
	}
	destory_stack();
}


//练习:使用链式栈实现10进制转8进制

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int SElemType_t;

struct node{
	SElemType_t data;
	struct node *next;
};

struct list_stack
{
	struct node *stack;
	int size;
};

struct list_stack *managerStack;

bool init_stack()
{
	managerStack = malloc(sizeof(struct list_stack));
	if(managerStack == NULL)
	{
		printf("malloc managerStack error\n");
		return false;
	}
	
	managerStack->size = 0;
	managerStack->stack = NULL;
	
	return true;
}

bool isEmpty()
{
	return managerStack->size==0;
}
//出栈--删除(头删)
bool pop(SElemType_t *outData)
{
	//1、先判断当前有没有栈元素
	if(isEmpty())
		return false;
	//2、先获取出栈的数据
	*outData=managerStack->stack->data;
	//先定义一个临时的指针存储当前删除结点的地址
	struct node*delNode=managerStack->stack;
	//更新首结点
	managerStack->stack=delNode->next;
	//释放
	free(delNode);
	//size--
	managerStack->size--;
	return true;
}
//销毁栈
void destory_stack()
{
	if(managerStack==NULL)
		return;
	//1.遍历所有的结点,每个点都删除
	struct node *p=managerStack->stack;
	struct node *pnext=NULL;
	while(p)
	{
		pnext=p->next;
		free(p);
		p=pnext;
	}
	//2.释放头结点(栈管理结构体)
	free(managerStack);
	managerStack=NULL;
}

bool push(SElemType_t inputData)
{
	struct node *newNode = malloc(sizeof(struct node));
	if(newNode==NULL)
	{
		printf("malloc newNode error\n");
		return false;
	}
	
	newNode->data = inputData;
	newNode->next = NULL;
	
	if(managerStack->stack == NULL)
	{
		managerStack->stack = newNode;
	}
	else
	{
		newNode->next = managerStack->stack;
		managerStack->stack = newNode;
	}
	
	managerStack->size++;
	
	return true;
}


void turn(SElemType_t data)
{
	if(data == 0)
		return;
	else
	{
		push(data%8);
		turn(data/8);
		return;
	}
	
}

int main(int argc,char *argv[])
{
	int data;
	init_stack();
	printf("pls input data:\n");
	scanf("%d",&data);
	turn(data);
	while(pop(&data))
	{
		printf("%d",data);
	}
	printf("\n");
	destory_stack();
	
	return 0;
}

一、队列
1、概念
    线性表的插入(入队)在指定的一端,删除(出队)必须在另外一端,不能在其他位置,这种线性表称之为队列。
    特性:先进先出
2、分类
    链式队列 顺序队列
3、设计数据结点和队列管理结构体
typedef int QElemType_t;
struct list_queue *managerQueue;//保存链式队列的管理结构体
//数据结点
struct node
{
    QElemType_t data;
    struct node *next;
};
//链式队列的管理结构体(头结点)
struct list_queue
{
    struct node *first;//队头--相当于之前数据的首结点
    struct node *last;//队尾--数据的尾结点
    int size;//元素的个数
};
//实现尾插头删(先进先出)
//入队--尾插(新建一个结点,插入到链表中)
bool enter_queue(QElemType_t inputdata)
{
    //1.新建结点,并且初始化
    struct node *newNode=malloc(sizeof(struct node));
    if(newNode==NULL)
    {
        printf("malloc newNode error\n");
        return false;
    }
    newNode->data=inputdata;
    newNode->next=NULL;
    //2.尾插
    //1)从无到有----刚开始没有数据结点,插入的第一个结点是数据的首结点也是尾结点
    if(managerQueue->first==NULL)
    {
        managerQueue->first=managerQueue->last=newNode;
    }
    //2)由少到多
    else
    {
        managerQueue->last->next=newNode;
        //更新尾结点
        managerQueue->last=newNode;
    }
    //3、链式队列的结点个数+1
    managerQueue->size++;
    return true;
}
//判断队列是否为空
bool isEmpty()
{
    return managerQueue->size==0;
}
bool leave_queue(QElemType_t *outdata)
{
    //1.判断是否为空
    if(isEmpty())
    {
        printf("isEmpty\n");
        return false;
    }
    //2、获取数据(从数据的首结点获取数据)
    *outdata=managerQueue->first->data;
    //3.删除结点
    //1)先保存当前删除结点的位置
    struct node *delNode=managerQueue->first;
    //2)更新首结点
    managerQueue->first=delNode->next;
    //3)断链接
    delNode->next=NULL;
    //4)释放删除结点
    free(delNode);
    //5)如果当前队列只有一个结点,此时删除之后,first last都要NULL
    if(managerQueue->first==NULL)
        managerQueue->last=NULL;
    //4.链式队列的结点个数-1
    managerQueue->size--;
    return true;
    
}
//销毁队列
void destory_queue()
{
    if(managerQueue==NULL)
    return;
    //遍历队列的所有结点
    struct node *p=managerQueue->first;
    struct node *pNext=NULL;
    while(p)
    {
        pNext=p->next;
        free(p);
        p=pNext;
    }
    //释放队列管理结构体
    free(managerQueue);
    managerQueue->NULL;
    
}

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int QElemType_t;
struct list_queue *managerQueue;//保存链式队列的管理结构体
//数据结点
struct node
{
	QElemType_t data;
	struct node *next;
};
//链式队列的管理结构体(头结点)
struct list_queue
{
	struct node *first;//队头--相当于之前数据的首结点
	struct node *last;//队尾--数据的尾结点
	int size;//元素的个数
};
//创建一条链式队列
bool init_queue()
{
	//1)申请队列的管理结构体内存空间
	managerQueue=malloc(sizeof(struct list_queue));
	if(managerQueue==NULL)
	{
		printf("managerQueue malloc error\n");
		return false;
	}
	//2)初始化
	managerQueue->first=NULL;
	managerQueue->last=NULL;
	managerQueue->size=0;
	return true;
	
}
//实现尾插头删(先进先出)
//入队--尾插(新建一个结点,插入到链表中)
bool enter_queue(QElemType_t inputdata)
{
	//1.新建结点,并且初始化
	struct node *newNode=malloc(sizeof(struct node));
	if(newNode==NULL)
	{
		printf("malloc newNode error\n");
		return false;
	}
	newNode->data=inputdata;
	newNode->next=NULL;
	//2.尾插
	//1)从无到有----刚开始没有数据结点,插入的第一个结点是数据的首结点也是尾结点
	if(managerQueue->first==NULL)
	{
		managerQueue->first=managerQueue->last=newNode;
	}
	//2)由少到多
	else
	{
		managerQueue->last->next=newNode;
		//更新尾结点
		managerQueue->last=newNode;
	}
	//3、链式队列的结点个数+1
	managerQueue->size++;
	return true;
}
//判断队列是否为空
bool isEmpty()
{
	return managerQueue->size==0;
}
bool leave_queue(QElemType_t *outdata)
{
	//1.判断是否为空
	if(isEmpty())
	{
		printf("isEmpty\n");
		return false;
	}
	//2、获取数据(从数据的首结点获取数据)
	*outdata=managerQueue->first->data;
	//3.删除结点
	//1)先保存当前删除结点的位置
	struct node *delNode=managerQueue->first;
	//2)更新首结点
	managerQueue->first=delNode->next;
	//3)断链接
	delNode->next=NULL;
	//4)释放删除结点
	free(delNode);
	//5)如果当前队列只有一个结点,此时删除之后,first last都要NULL
	if(managerQueue->first==NULL)
		managerQueue->last=NULL;
	//4.链式队列的结点个数-1
	managerQueue->size--;
	return true;
	
}
//销毁队列
void destory_queue()
{
	if(managerQueue==NULL)
	return;
	//遍历队列的所有结点
	struct node *p=managerQueue->first;
	struct node *pNext=NULL;
	while(p)
	{
		pNext=p->next;
		free(p);
		p=pNext;
	}
	//释放队列管理结构体
	free(managerQueue);
	managerQueue=NULL;
	
}
void main()
{
	int outdata;
	init_queue();
	//入队
	enter_queue(10);
	enter_queue(20);
	enter_queue(30);
	enter_queue(40);
	enter_queue(50);
	//出队
	while(leave_queue(&outdata))
	{
		printf("%d\t",outdata);
	}
	printf("\n");
	destory_queue();
	
}

 


二、顺序队列的实现
顺序队列:队列中每个元素的内存空间都是连续的
typedef int QElemType_t;
struct sequent_queue *managerQueue;//保存顺序队列的管理结构体
//数据结点
struct node
{
    QElemType_t data;
    struct node *next;
};
//顺序队列的管理结构体(头结点)
struct sequent_queue
{
    QElemType_t *queue;//保存连续内存地址的首地址
    int first;//队头
    int last;//队尾
    int size;//队列元素的总个数
};

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int QElemType_t;
struct sequent_queue *managerQueue;//保存顺序队列的管理结构体
//数据结点
struct node
{
	QElemType_t data;
	struct node *next;
};
//顺序队列的管理结构体(头结点)
struct sequent_queue
{
	QElemType_t *queue;//保存连续内存地址的首地址
	int first;//队头
	int last;//队尾
	int size;//队列元素的总个数
};
//创建一个顺序队列并初始化
bool init_queue(int size)
{
	//1.申请队列的管理结构体的内存空间
	managerQueue=malloc(sizeof(struct sequent_queue));
	if(managerQueue==NULL)
	{
		printf("malloc managerQueue error\n");
		return false;
	}
	//2.初始化
	managerQueue->queue=NULL;
	managerQueue->first=managerQueue->last=0;
	managerQueue->size=size;
	//3.申请一片连续的内存空间存储队列元素,其首地址给queue存储
	managerQueue->queue=malloc(sizeof(QElemType_t)*size);
	if(managerQueue->queue==NULL)
	{
		free(managerQueue);
		managerQueue=NULL;
		printf("malloc managerQueue->queue error\n");
		return false;
	}
	return true;
}
bool isFull()
{
	return (managerQueue->last+1)%(managerQueue->size)==managerQueue->first;	
}
//入队---尾插
bool enter_queue(QElemType_t inputdata)
{
	//1.先判断当前顺序队列是否是满的
	if(isFull())
	{
		printf("isFuLL\n");
		return false;
	}
	//2.将数据插入到队列中
	managerQueue->queue[managerQueue->last]=inputdata;
	//3.队尾往后面偏
	managerQueue->last=(managerQueue->last+1)%managerQueue->size;
	return true;
}
bool isEmpty()
{
	return managerQueue->last==managerQueue->first;
}
//出队
bool leave_queue(QElemType_t *outdata)
{
	//1.判断是否为空
	if(isEmpty())
	{
		printf("isEmpty\n");
		return false;
	}
	//2.获取数据first
    *outdata=managerQueue->queue[managerQueue->first];
	//3.first队头,往后进行偏移
	managerQueue->first=(managerQueue->first+1)%managerQueue->size;
	return true;
}
//销毁队列
void destory_queue()
{
	//1.先释放连续的内存空间
	if(managerQueue->queue)
		free(managerQueue->queue);
	//2.释放队列管理结构体
	if(managerQueue)
		free(managerQueue);
	managerQueue=NULL;	
}
void main()
{
	init_queue(7);
	enter_queue(10);
	enter_queue(20);
	enter_queue(30);
	enter_queue(40);
	enter_queue(50);
	enter_queue(60);
	enter_queue(70);
	QElemType_t outdata;
	while(leave_queue(&outdata))
	{
		printf("%d\n",outdata);
	}
	printf("\n");
	destory_queue();
	return ;
}

 

 

 三、排序
 排序是处理数据的一种常见的操作,所谓的排序是将数据按某个字段规律排列,
 所说的字段就是数据结点的其中一个属性
 稳定性:在一组无序的数据中,若两个待排序的数据,在排序前后的相对位置没有发生改变,称之为稳定排序
 时间复杂度
 空间复杂度
 不同的排序算法性能不同(选择排序、插入排序、希尔排序、冒泡排序)

 四、算法的复杂度
 1、概念
  算法指的是用来操作数据、解决问题的一组方法。比如对于同一个问题,
  使用不同的算法都能够去实现,得到的结果也一样,但是在这个过程中消耗的资源以及时间会有很大区别。
  主要从算法在运行过程中的“时间”和“空间”两个维度去衡量
 2、分类
 时间复杂度:是指当前算法或者某一段程序所消耗的时间
 空间复杂度:是指执行算法需要占用的内存空间
 3、时间复杂度
 1)如何计算时间复杂度
 取决于这个算法或这段程序的执行次数,并且采用估算值。执行的次数越少,说明使用的时间越少
 2)案例
 int func1()
 {
    printf("11111\n");
    printf("11111\n");
    return 0;
 }
  
  int func2(int n)
 {
    for(int i=0;i<n;i++)
    printf("11111\n");
    
 }
 
 一个算法花费的时间与算法中语句的执行次数成正比,执行次数越多,花费的时间就越多。
 一个算法中的执行次数称为语句频度或时间频度。记为T(n);
 在各种算法中,若算法中的语句执行次数为一个常数,则时间复杂度为o(1)
 
 假设每行代码的执行时间是一样的,那么我们调用一次fun1()函数
 执行次数:3次 所以T(n)=3=O(1);
 n表示输入的参数,当n无穷大时,时间复杂度还是3次,所以规定时间复杂度为常数时,
 时间复杂度的估算值为O(1)
 调用一次func2函数,执行次数:3n+2次 ,所以T(n)=3n+2=O(n);
 
 例子:T(n)=5*n^4+44*n^2+n+55=O(n^4)
 
 总结:取决于T(n)是不是常数
       如果是:时间复杂度O(1)
       如果不是:时间复杂度为O(T(n)的最高次幂)
       
 
int fun3(int n)
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
            printf("11111\n");
    }

int fun4(int n)
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
            printf("11111\n");
    }
    for(int i=0;i<n;i++)
    printf("11111\n");
}
int fun5(int n)
{
    if(n>200)
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            printf("11111\n");
        }
    
    }
    else
    {
        for(int i=0;i<n;i++)
        printf("11111\n");
    }
    

调用func3函数一次:里面循环n次,外面的循环也是n,T(n)=O(n^2)
调用func4函数一次:T(n)=O(n^2)
调用func5函数一次: T(n)=O(n^2)

int fun6(int n)
{
    for(int i=0;i<n;i++)
    {
        for(int j=i;j<n;j++)
        printf("11111\n");
    }
}
当i=0,里面循环n
当i=1,里面n-1
  i=2    n-2
  i=n-1   1
相加(等差数列的求和公式)
1+2+3+......+(n-1)+n-------->(n*(n+1))/2 =(n^2+n)/2
所以T(n)=O(n^2)

int fun7(int n)
{
    for(int i=1;i<n;i*=2)
    {
        printf("11111\n");
    }
}
当n=8时,printf函数执行次数为3   T(8)=3
当n=16时,printf函数执行次数为4  T(16)=4
T(8)=3--------->2^3=8
T(16)=4-------->2^4=16    2^T(n)=n   

T(n)=log以2为底n的对数   

4、空间复杂度
空间复杂度是对算法过程中临时占用内存空间的度量。
空间复杂度:S(n)=O()
void func1(int n)
{
    n++;
    int a=2;
    int b=3;
}
调用一次fun1函数,空间复杂度为S(n)=O(1)
void func2(int n)
{
    char*p=malloc(n);
    for(int i=0;i<n;i++)
    {
        p[i]=i;
    }
}   
调用一次fun2函数,空间复杂度为S(n)=O(n);
void fun3(int n)
{
    char ch[n][n];
}
调用一次fun3函数,空间复杂度为S(n)=O(n^2);

5、选择排序
    首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕

#include<stdio.h>
void swap(int *a,int *b) //交換兩個變數
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
//选择排序
void selection_sort(int arr[], int len)
{
    int i,j;

        for (i = 0 ; i < len - 1 ; i++)
        {
                int min = i;
                for (j = i + 1; j < len; j++)     //走訪未排序的元素
                        if (arr[j] < arr[min])    //找到目前最小值
                            min = j;    //紀錄最小值
                swap(&arr[min], &arr[i]);    //做交換
        }
}
//插入排序
void insertion_sort(int arr[], int len)
{
        int i,j,key;
        for (i=1;i<len;i++){
                key = arr[i];
                j=i-1;
                while((j>=0) && (arr[j]>key)) 
				{
                        arr[j+1] = arr[j];
                        j--;
                }
                arr[j+1] = key;
        }
}
void show(int *arr,int len)
{
	int i;
	for(i=0;i<len;i++)
	{
		printf("%d\t",arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[]={15,23,5,6,18};
	int len=sizeof(arr)/sizeof(arr[0]);
	//selection_sort(arr,len);
	insertion_sort(arr,len);
	show(arr,len);
	return 0;
}


6、插入排序
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。
(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
 

 


 


版权声明:本文为weixin_48102054原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。