数据结构--链表

    定义
        n个节点离散分配
        彼此通过指针相连
        每个节点只有一个前驱节点,每个节点只有一个后继节点
        首节点没有前驱节点,尾节点没有后续节点
    专业术语
        链表中每个节点的数据类型是一样的
        首节点:链表第一个有效的节点
        尾节点:最后一个有效的节点
        头节点:第一个有效节点之前的节点,头节点并不存放有效数据
        头指针:指向头节点的指针变量
        尾指针:指向尾节点的指针变量
    确定一个链表只需要一个参数,
        只需要头指针,就可以推算列表的其它所有信息
    用程序表示每一个节点的数据类型

    #include <stdio.h>

    typedef struct Node{
        int data;//data
        struct Node* pNode;//point,指向的每个节点的数据类型一样
    }Node,*PNode; //在*和前面是一个整体,Node相当于struc Node,PNode相当于 struct Node *

    int main(void){

        return 0;
    }

    链表的分类
        单链表
        双链表:每个链表有两个指针域
        循环列表:能通过任意节点找到其它节点
        非循环链表
    链表是否为空的判断
        思路:判断头节点的指针域是不是为空 p->pNext
    求链表的长度
        思路:遍历列表,直到找到最后一个指针域为NULL的节点,累计遍历的次数
    对链表排序
        思路:参考冒泡排序的思路,利用反射技术,可以使链表的排序和数组的排序一样,即
            i=0(第一个有效节点的位置)等价于pHead->pNext;i<length-1;i++相当于p->pNext  
链表的插入

       问题:在a节点(指针为p)后面插入一个节点b(指针为q)的伪算法
     思路:要在a后面插入,那么a就指向b,由原来的a指向c变为b指向c。q->pNext(b的指针域)存放p->pNext的值,p->pNext(a的指针域)指向存放q的值。  

 q->pNext=p->pNext
 p-pNext=q

 链表的删除
        问题:把a节点(指针为p)后的b节点删除
        思路:即把b节点存放的c的地址给a节点,即 p->pNext=p->pNext->pNext.在java中,这样写是可以的,但是c语言中,内存无法自己释放,此时p->pNext 的值已经改变,找不到b节点的位置了,这种算法会导致内存泄漏。我们可以定义一个r指针来临时存放解决这个问题
        伪代码:

p->pNext=p->pNext->pNext(内存泄漏)
r=p->pNext;//(先存放b的地址)
p->pNext=p->pNext->pNext;
free r;//释放内存

链表的创建和链表的遍历算法    
    生成
        生成所有节点(用一个pNew指针指向所有节点,每次循环指向一个节点)
        生成一个头节点,把头节点的指针赋给pTail
        把新生成的节点做为尾节点,挂在头节点后面,让pTail永远指向pNew(新节点的指针)
    遍历
        把头节点的指针赋给p,p指向下一个节点,当下一个节点不为空,则继续往下指,当节点为空时,代表走到了尾部。

#include <stdio.h>
#include <malloc.h>
 
typedef struct Node{
	int data;//数据域
	struct Node * pNext;//指针域
}NODE ,*PNODE; //NODE 等价于 struct Node PNODE 等价于 struct Node * 
 
//函数声明
PNODE creat_list(void); //创建链表
bool isNull(PNODE pHead);//判断链表是否为空
void traverse_list(PNODE pHead);//遍历输出链表
int length(PNODE pHead);//求链表长度
void sort(PNODE,int ,int);//对链表排序:冒泡排序


int main(void){
 
	int i;
	PNODE pHead = NULL; //等价于 struct Node  * PHean = null;
	pHead  = creat_list();//creat_list():创建一个非循环的单链表,并把头节点的地址赋pHead.	//动态内存可以通过在别的函数中分配好在引用
	if(	isNull(pHead)){
		printf("链表不为空\n");
	}else {
		printf("链表为空\n");
	}
	traverse_list(pHead);
	i =	length(pHead);
	printf("链表的长度是%d\n",i);
 
	return 0;
}
 
PNODE creat_list(void){
 
	int len;//节点个数
	int i;
	int val;//节点值
	//为头节点分配内存
	PNODE pHead=(PNODE)malloc(sizeof(NODE));
	if(NULL==pHead){
		printf("分配内存失败,程序终止!");
		
	}
	//定义一个指向头指针
	PNODE pTail=pHead;
	pTail->pNext=NULL;
 
	printf("请输入需要生成的链表节点的个数,len=");
	scanf("%d",&len);
	for( i=0;i<len;i++){
			printf("请输入第%d个节点的值:",i+1);
			scanf("%d",&val);
		//为每个节点分配内存
		PNODE pNew = (PNODE)malloc(sizeof(NODE));
		if(NULL==pHead){
			printf("分配内存失败,程序终止!");
		
		}
		pNew->data=val;//用一个指针指向所有的节点。注意:每次pNew都指向不同的节点
		pTail->pNext=pNew;//pNew挂到pTail后面
		pNew->pNext = NULL;//把新节点设为尾部节点
		pTail=pNew;//pTail永远指向尾节点
	}
	return pHead;
}
 
void traverse_list(PNODE pHead){
	PNODE p=pHead->pNext;//指向头节点的下一个有效节点
	while(NULL!=p){
		printf("%d ",p->data);
		p=p->pNext;
	}
	printf("\n");	
}

bool isNull(PNODE pHead){
	if(NULL==pHead->pNext){
		return false;
	}
	return true;

}

int length(PNODE pHead){
	int i = 0;
	PNODE P=pHead->pNext;
	while(NULL!=P->pNext){
		i++;
		P=P->pNext;
	}
	return i;
}

void sort(PNODE pHead){

	int i,j,t;
	PNODE q,r;

}

   冒泡算法:对一个数组排序(升序)。把第一个元素和第二个元素比较,小的放前面,大的放后面;然后第一个和第三个比较,直到第一个和最后一个比较。以此循环。
    代码为:

    int i,j,t,length;
    for(i=0;i<length-1;i++){//每个元素都需要单独取出来,与后面的元素比较。最后一个不需要取出来比较,故i<length-1
        for(j=i+1;j<length;j++){//第i个元素依次和后面的元素比较
            if(a[i]>a[j]){
                t=a[j];
                a[j]=a[i];//交换位置,把大的放后面去
                a[i]=t;
            }  
        }
    }


    算法
        遍历
        查找
        清空
        销毁
        求长度
        排序
        删除节点
        新增节点
    广义的算法和数据的存储方式无关
    侠义的算法与数据存储方式密切相关
    泛型:利用某种技术达到效果就是:不同的存数方式,执行的操作是一样的

    
    
   

猜你喜欢

转载自blog.csdn.net/stonennnn/article/details/81316397