数据结构和算法
单链表
struct Node
{
int data;
Node *nextNode;
};
创建列表
Node *CreatList()
{
Node *head,*p,*s;
int cycle=1,x;
int count=0;
head=(Node*)malloc(sizeof(Node));
p=head;
while(cycle)
{
s=(Node*)malloc(sizeof(Node));
printf("Input value:\n");
scanf("%d",&x);
if (0==x)
{
break;
}
else
{
s->data=x;
p->nextNode=s;
p=s;
count++;
}
}
p->nextNode=NULL;
head->data=count;
return head;
}
链表删除
Node* DeleteNode(Node*Head,int value)
{
Node* p1,*p2;
p1=Head;
while(value!=p1->data && p1->nextNode!=NULL)
{
p2=p1;
p1=p1->nextNode;
}
if (value==p1->data)
{
if (p1==Head)
{
Head=p1->nextNode;
free(p1);
}
else
{
p2->nextNode=p1->nextNode;
}
}
return Head;
}
链表插入
Node* InsertNode(Node* Head,int index,int value)
{
Node *p1,*p2,*s;
int count=1;
p1=Head;
s=(Node*)malloc(sizeof(Node));
s->data=value;
while (count<index && p1->nextNode!=NULL)
{
p2=p1;
p1=p1->nextNode;
count++;
}
if (count==index && p1->nextNode!=NULL)
{
if (Head==p1)
{
s->nextNode=p1;
Head=s;
}
else
{
p2->nextNode=s;
s->nextNode=p1;
}
}
if (count==index && p1->nextNode==NULL)
{
p1->nextNode=s;
s->nextNode=NULL;
}
return Head;
}
链表逆置
Node* Reverse(Node *Head)
{
Node *p1,*p2,*p3;
if (Head==NULL && Head->nextNode==NULL)
{
return Head;
}
p1=Head;
p2=p1->nextNode;
while(p2!=NULL)
{
p3=p2->nextNode;
p2->nextNode=p1;
p1=p2;
p2=p3;
}
Head->nextNode=NULL;
Head=p1;
return Head;
}
队列(先进先出FIFO)
typedef struct linkqueue
{
Node *front,*rear;
}queue;
入队
queue *insert(queue *HQ,int val)
{
Node *s;
s=(Node*)malloc(sizeof(Node));
s->data=val;
s->nextNode=NULL;
if (HQ->rear==NULL)
{
HQ->front=s;
HQ->rear=s;
}
else
{
HQ->rear->nextNode=s;
HQ->rear=s;
}
return HQ;
}
出队
queue *del(queue *HQ)
{
Node *p;
if (HQ->front!=NULL)
{
p=HQ->front;
if (HQ->front==HQ->rear)
{
HQ->front=NULL;
HQ->rear=NULL;
}
else
{
HQ->front=HQ->front->nextNode;
free(p);
}
}
return HQ;
}
排序
快速排序
基本思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
int Partition(int arr[],int low,int high)
{
int pivotKey=arr[low];
while(low<high)
{
while (low<high && arr[high]>=pivotKey)
{
high--;
}
swap(arr[low],arr[high]);
while (low<high && arr[low]<=pivotKey)
{
low++;
}
swap(arr[low],arr[high]);
}
return low;
}
void QSort(int arr[],int low,int high)
{
int pivot=0;
if(low<high)
{
pivot=Partition(arr,low,high);
QSort(arr,low,pivot-1);
QSort(arr,pivot+1,high);
}
}
选择排序
基本思想:每一趟在n-i+1个记录中选取关键字最小的记录作为有序序列的第i个记录。它最大的特点就是交换移动数据次数相当少。
void SelectSort(int arr[],int len)
{
int min=0;
for (int i=0;i<len-1;i++)
{
min=i;
for (int j=i+1;j<len;j++)
{
if (arr[j]<arr[min])
{
min=j;
}
}
if (min!=i)
{
swap(arr[min],arr[i]);
}
}
}
插入排序
void InsertSort(int a[],int len)
{
int i,j;
for (i=1;i<len;i++)
{
if (a[i]<a[i-1])
{
int temp=a[i];
for(j=i-1;j>=0 && a[j]>temp;j--)
{
a[j+1]=a[j];
}
a[j+1]=temp;
}
}
}
堆排序
void HeapAdjust(int a[],int len)
{
int temp;
int childIndex;
int count=(len-1)/2;
for (int i=count;i>=0;i--)
{
temp=a[i];
childIndex=2*i+1;
if (childIndex<len && a[childIndex]<a[childIndex+1])
{
childIndex++;
}
if (temp<a[childIndex])
{
swap(a[i],a[childIndex]);
}
}
}
void HeapSort(int a[],int len)
{
for(int i=len-1;i>0;i--)
{
HeapAdjust(a,i);
swap(a[0],a[i]);
}
}
归并排序
归并排序是分治法的一个非常典型的应用,且各层分治递归可以同时进行。其核心思想是将两个有序的数列合并成一个大的有序的序列,通过递归,层层合并,即为归并。
字符串
整数转为字符串
整数加‘0’隐形转化为char类型的数
void itoa(int n,char s[])
{
char temp[11];
int index=0;
int sign=0;
int j=0;
if (n<0)
{
n=-n;
sign=-1;
}
while(n&&index<=10)
{
temp[index++]=n%10+'0';
n=n/10;
}
temp[index]=NULL;
index=index-1;
if (sign<0)
{
s[j]='-';
j++;
}
while (index>=0)
{
s[j]=temp[index];
index--;
j++;
}
s[j]=NULL;
}
字符串转为整数
int atoi(char s[])
{
int i=0;
int sum=0;
int sign=1;
if (s[i]=='-')
{
sign=-1;
i++;
}
while(s[i]!=NULL)
{
if ((s[i]<'0')||(s[i]>'9'))
{
break;
}
sum=sum*10+(s[i]-'0');
i++;
}
sum=sign*sum;
return sum;
}
strcpy
char *strcpy(char *destStr,char *srcStr)
{
char *address;
address=destStr;
if (destStr==NULL && srcStr==NULL)
{
return NULL;
}
while((*destStr++=*srcStr++)!='\0')
NULL;
return address;
}
strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值?
答:为了实现链式表达式。
例如 int length = strlen( strcpy( strDest, “hello world”) );
串拷贝(strcpy)和内存拷贝(memcpy)有什么不同?它们适合于在哪种情况下使用?
strcpy()函数只能拷贝字符串,将源字符串的每个字节拷贝到目的字符串中。当遇到字符串末尾的NULL字符(\0)时,他会删除该字符,并结束拷贝。
memcpy()函数可以拷贝任意类型的数据。因为并不是所有的数据都是以NULL字符结束,所以要为memcpy()函数指定要拷贝的字节数。
在拷贝字符串时,通常使用strcpy()函数,在拷贝其他数据(如结构)时,通常使用memcpy()
memcpy
void *memcpy(char *dest,char *src,size_t size)
{
if (dest==NULL && src==NULL)
{
return NULL;
}
char *tempSrc=src;//防止改变src
char *tempDest=dest;
while(size-->0)
*tempDest++=*tempSrc++;
return dest;
}
计算字符串中单词数量
int CountWords(char *str)
{
int count=0;
int flag=0;
if (str==NULL)
{
return count;
}
else
{
while(*str!=NULL)
{
if (*str==' ')
{
flag=0;
}
else if(flag==0)
{
flag=1;
count++;
}
str++;
}
}
return count;
}
字符串逆序
char* ReverseStr(char *str)
{
char *p,*q;
p=str;
q=str;
while(*q)
{
q++;
}
q--;
while (q>p)
{
char t=*p;
*p++=*q;
*q--=t;
}
return str;
}
按单词逆序
思路:按单词逆序,然后将整个句子作为整体之后再逆序一次
void ReverseWord(char *p,char *q)
{
while(p<q)
{
char t;
t=*p;
*p++=*q;
*q--=t;
}
}
char* Reverse(char*str)
{
char *p;
char *q;
p=str;
q=str;
while(*q!=NULL)
{
if (*q==' ')
{
ReverseWord(p,q-1);
q++;
p=q;
}
else
q++;
}
ReverseWord(p,q-1);
ReverseWord(str,q-1);
return str;
}
二叉树
创建树
void CreateTree(BiTree *tree,int value)
{
if (*tree==NULL)
{
BiTree node=(BiTNode*)malloc(sizeof(BiTNode));
node->data=value;
node->lChild=NULL;
node->rChild=NULL;
*tree=node;
return;
}
else if (value<(*tree)->data)
{
CreateTree(&(*tree)->lChild,value);
}
else if (value>(*tree)->data)
{
CreateTree(&(*tree)->rChild,value);
}
else
{
}
}
遍历树
/*前序遍历*/
void PreOrderTraverse(BiTree T)
{
if (T==NULL)
{
return;
}
else
{
int data;
data=T->data;
cout<<data<<endl;
PreOrderTraverse(T->lChild);
PreOrderTraverse(T->rChild);
}
}
/*中序遍历*/
void InOrderTraverse(BiTree T)
{
if (T==NULL)
{
return;
}
else
{
int data;
data=T->data;
InOrderTraverse(T->lChild);
cout<<data<<endl;
InOrderTraverse(T->rChild);
}
}
/*后序遍历*/
void PostOrderTraverse(BiTree T)
{
if (T==NULL)
{
return;
}
else
{
char data;
data=T->data;
PostOrderTraverse(T->lChild);
PostOrderTraverse(T->rChild);
cout<<data<<endl;
}
}
已知前序、中序求后序
void initTree(BiTree *root,char* front,char*midlle,int num)
{
int i=0;
(*root)->data=front[0];
if (num==0)
{
return;
}
//找到根节点在middle所在的位置
for (i=0;i<num;i++)
{
if ((*root)->data==midlle[i])
{
break;
}
}
if (i!=0)//如果root存在左孩子
{
(*root)->lChild=new struct BiTNode();
initTree(&(*root)->lChild,front+1,midlle,i);
}
if (i!=num-1)//如果root存在右孩子
{
(*root)->rChild=new struct BiTNode();
initTree(&(*root)->rChild,front+i+1,midlle+i+1,num-i-1);
}
return;
}
非递归实现前、中、后序遍历
//前序
void PreOrder(BiTNode *root)
{
stack<BiTNode*> stackNode;
BiTNode *cur=root;
while (cur!=NULL||!stackNode.empty())
{
//每次将当前结点访问了,然后入栈
//当while循环结束的时候,cur指向最左结点的左结点为NULL
while (cur!=NULL)
{
cout<<cur->data<<endl;
stackNode.push(cur);
cur=cur->lChild;
}
//while循环出来表示整个数的根结点和其左子树的根结点均已被打印;
//现在要做的是从下往上一次打印左右节点;
if (!stackNode.empty())
{
cur=stackNode.top();//取出最左结点;
stackNode.pop();//将最左结点出栈
cur=cur->rChild;
}
}
}
//中序
void InOrder(BiTNode *root)
{
stack<BiTNode*>stackNode;
BiTNode *cur=root;
while(cur!=NULL||!stackNode.empty())
{
while(cur!=NULL)
{
stackNode.push(cur);
cur=cur->lChild;
}
if (!stackNode.empty())
{
cur=stackNode.top();
cout<<cur->data<<endl;
cur=cur->rChild;
}
}
}
//后序
void PostOrder(BiTNode *root)
{
BiTNode *cur=root;//当前节点
BiTNode *pre=NULL; //上一次打印的节点
stack<BiTNode*> stackNode;
//只有当前节点或者栈中还有元素,则该二叉树一定还没有打印完
while(cur!=NULL||!stackNode.empty())
{
while(cur!=NULL)//一直找到最左节点
{
stackNode.push(cur);
cur=cur->lChild;
}
if (!stackNode.empty())
{
BiTNode *node=stackNode.top();//取出最左节点
//如果节点的右孩子为空,或者右孩子已经被打印,则可以打印本节点
if (node->rChild==NULL||node->rChild==pre)
{
cout<<node->data<<endl;
stackNode.pop();
pre=node;//将pre更新为已经打印过的节点
}
//如果节点的右孩子不为空,且没有被访问过,则将cur更新为右孩子,继续whiile循环
else
{
cur=node->rChild;
}
}
}
}
递归方式计算树的深度
采用后序遍历,先计算左子树的深度,再计算右子树的深度,最后取较大者加1即为二叉树的深度。
int PostOrderTreeDepth(BiTNode *root)
{
int leftDepth,rightDepth,maxDepth;
if(root!=NULL)
{
leftDepth=PostOrderTreeDepth(root->lChild);
rightDepth=PostOrderTreeDepth(root->rChild);
maxDepth=leftDepth>rightDepth?leftDepth:rightDepth;
return (maxDepth+1);
}
else
{
return 0;
}
}
非递归计算数的深度
采用层次遍历的方法:
①每遍历一层,depth++
②每一层,用一个变量len记录该层的节点个数,也就是队列的当前长度,然后依次在队列中访问该层的len个节点(将队列中len个元素出队列),并将下一层入队列。
int TreeDepth(BiTNode *root)
{
queue <BiTNode*> q;
int level=0;
int len;
BiTNode *temp;
if (root==NULL)
{
return 0;
}
q.push(root);
while (!q.empty())
{
level++;
len=q.size();
while(len--)
{
temp=q.front();
q.pop();
if (temp->lChild)
{
q.push(temp->lChild);
}
if (temp->rChild)
{
q.push(temp->rChild);
}
}
}
return level;
}
算法
topK
利用堆排序,建立一个大小为k的大顶堆,遍历n个数字,若比堆顶元素小,则取代之,并将堆更新保持大顶堆。 那么只需要遍历一次n,然后输出大小为k的最终的大顶堆。 即为最小的k个数字。O(nlogk)
void HeapAdjustTest(int a[],int len)
{
int index=len/2-1;
for (int i=index;i>=0;i--)
{
int childIndex=2*i+1;
if (childIndex<len && childIndex+1<len)
{
if (a[childIndex]>a[childIndex+1])
{
childIndex++;
}
}
if (a[i]>a[childIndex])
{
swap(a[i],a[childIndex]);
}
}
}
void topK(int input[],int output[],int n,int k)
{
int min;
for (int i=0;i<k;i++)
{
output[i]=input[i];
}
HeapAdjustTest(output,k);
min=output[0];
for(int j=k;j<n;j++)
{
if (input[j]>min)
{
swap(output[0],input[j]);
HeapAdjustTest(output,k);
min=output[0];
}
}
}