排序算法:
完成比完美更重要!
题目中常考的是平均时间复杂度:但是具体计算时,能用最坏就用最坏
插入:直接插,希尔
交换:冒泡,快排
选择:简单选择,堆排
归并,二路归并,基数
为了高分,一般选择希尔,堆排,快排,但是堆排的代码不好写,希尔的复杂度不好分析,一般代码提只考虑快排
- 什么时候用快排:
- 顺序表,数组
- 乱序数组,并且排序号有利于解决问题
//快速排序基本代码
int partition(int A[],int L,int R){
int mid = A[L];
while(L<R){
while(A[R]>=mid) R--;//右指针从右往左移
A[L] = A[R];//当<mid时,退出循环,和R交换位置
while(A[L]<=mid) L++;
A[R] = A[L];
}
A[L] = mid;
return L;
}
void quick_sort(int A[],int L,int R)
{
if(L >= R) return;
int M = partition(A,L,R);
quick_sort(A,L,M-1);
quick_sort(A,M+1,R);
}
/*(1)折半查找(最优),顺序查找,归并排序,快速排序
本题采用快排:(1)两个数组合并成一个大数组
(2)对大数组进行排序(Qsort)
(3)找到中间元素
*/
int partition(int A[],int L,int R){
int pivot = A[L];
while(L<R){
while(A[R]>=mid) R--;
A[L] = A[R];
while(A[L]<=mid) L--;
A[R] = A[L];
}
mid = A[L];
}
void Qsort_Judge(int A[],int L,int R){
int M = partition(A,L,R);
if(M==
Qsort_Judge
}
(1),先将数组排序好
(2),找中间的元素,遍历数组,count中间元素的值
(3),如果count>2/n
Qsort(A,0,n-1);
int x = A[n/2];
for((int i = n/2-1,i>=0;i--){
if(A[i] == x){
count++;
}
}
比较好的方法:投票算法:
定义一个候选值candidata,一个count
如果count == 0
把第一个元素设置为candidat,
遍历,如果下一个元素==candidate count++
如果下一个元素 !=candidate count--
for (int num : nums) {
if (count == 0) {
candidate = num;
count = 1;
} else if (num == candidate) {
count++;
} else {
count--;
}
}
往快排上面想一下:
(1)乱序,所以先利用快排排序
(2)遍历找到第一个大于0的数
(3)如果那个数>=2,那么结果就是1
(4)如果那个数=1.让它往后遍历,同时定义一个m++,直到两者不相等
(4ii) 或者,遍历,比较差值插值>2,就返回
k 为第一个大于0的数组位置
for(int m = k+1;m<n;m++){
if(A[m] - A[m-1] >1)
return A[m
}
划分函数的意义:
划分函数返回pivot在数组中的位置
利用划分函数可以找到数组中第k小或者第k大的元素(如果划分结果是k)
找到数组中更小的或者更大的k个元素(如果划分结果是k)
把数组用划分为左右两个部分
先利用一次划分返回值m
如果k<m,对(0,m-1)的元素再次划分
利用划分函数找到数组中第k小的元素
int func(int A[],int n,int k){
while(1){
int M = partition(A,L,R);
if(M==k-1) break;
else if(M>k-1) R = M-1;
eles if(M<k-1) L = M+1;
}
return A[k-1];
}
利用划分函数找到数组中第k小的元素
int func(int A[],int n,int k){
while(1){
int M = partition(A,L,R);
if(M==k) break;
else if(M>k) R = M-1;
eles if(M<k) L = M+1;
}
return A[k-1];
}
利用划分的思想,找到第n/2的位置
二路归并函数思想运用(空间复杂度(O(n))
- 什么时候用归并
- 合并数组(有序的)
- 合并好之后是有序的
- 归并函数基本功
int Merge(int A[],int N,int B[],int M,int C[]){
while(i<M&&j<N){
if(A[i]<B[j]{
C[k++] = A[i++];
}
if(B[j]<A[i]{
C[k++] = B[j++];
}
//复制操作
while(i<M){
C[k++] = A[i++];
}
while(j<N){
C[k++] = B[j++];
}
return 1;
}
(1)归并排序(O(logn))
(2)找(N+M)/2
链表算法题:灵活度低,回归基础
//定义单链表结点
typedef struct LNode{
int data;
struct LNode *next;
} LNode,*LinkList;
//求单链表的长度
int get_length(Linklist L){
int length = 0;
LNode * p = L->next;
while(p!=NULL){
length++;
p = p->next;
}
cout<<"链表长度为"<<length<<endl;
return length;
}
//返回单链表的中间结点
LNode getMidNode(LinkList L){
int length = 0;
LNode * p = L->next;
while(p!=NULL){
length++;
p = p->next;
}
int n = p/2;
return getnNode(L,n);
}
// 返回单链表第n个结点
LNode getnNode(LinkList L,int n){
LNode *p = L->next;
int count = 0;
while(p!=NULL){
count++;
if(count==n)
break;
p = p->next;
}
return p;
}
// 倒数第k个结点:快慢指针,快指针先走k步,之后快指针和慢指针
再一起走
//
1,分别计算str1,str2的长度m,n,以及他们的差值k
2,p指针从str1,开始向后走k步
3,q指针从str2开始,与p指针现在的位置共同走,边走边判断两者是
否相等
LNode getLastSameLetter(LinkList L1,LinkList L2){
int m = get_length(L1);
int n = get_length(L2);
int k = m-n;
int count = 0;
if(k>=0){
LNode *p = getnNode(L1,k);
q = L2;
}
else{
LNode *q = getnNode(L2,k);
q = L1->next;
}
while(p!=NULL&&q!=NULL){
if(p = q)
break;
p = p->next;
q = q->next;
}
return p;
}
按关键字条件查找删除(前后指针)
删除操作,用双指针,删除p
void deleteX(LinkList L,int x){
LNode pre = L->next;
LNode p = pre->next;
while(p!=NULL){
if(p->data == x){
LNode *q = p;
p = p->next;
q ->next = p;
free(q);
}
else{
pre = p;
p = p->next;
}
}
}
插入操作,用双指针,插入x
在关键字递增有序的单链表中插入新的关键字,确保插入后单链表
递增有序
void insert(LinkList L,int x){
LNode pre = L->next;
Lnode p = pre -> next;
LNode temp = new LNode(x,NULL);
temp ->data = x;
while(p!=NULL){
if(p->data >x){
pre->next = temp;
temp ->next = p;
}
else{
pre = pre->next;
p = p ->next;
}
if(p == NULL){
pre ->next = temp;
}
}
}
1,需要找到绝对值相等的结点
2,需要双指针进行删除
3,如果题目中说时间复杂度尽可能高的算法,那么可以考虑空间换时
间,也就是,用一个数组进行记录,出现了多少次
void removeDuplicateNode(LinkList L,int a){
int count[n+1];
for(int i = 0;i<=n;i++){
count[i] = 0;
}
LNode pre = L;
LNode p = pre->next;
while(p!=NULL){
if(count[abs(p->data)]==0)
count[abs(p->data)] = 1;
else if(count[abs(p->data)]==1){
LNode temp = p;
p = p->next;
pre->next = p;
free(p);
}
pre = pre->next;
p = pre->next;
}
}
头插法:实现链表的原地逆置
尾插法:保持原顺序
尾插法
设置两个链表,与一个计数器
计数器为奇数采用尾插法进A
计数器为偶数采用头插法进B
设置两个链表头,与一个计数器
计数器<n/2的,把数据拆下来,采用尾插法进A
计数器>n/2的,把数据拆下来,采用头插法进B
在原始L,依次拆下来A,B,进入L
二叉树的算法题:
typedef struct BiTNode{
int data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
int treeHeight = 0;
int max(int a,int b){
return ( a > b) ? a:b;
}
//前序遍历
void PreOrder(BiTree root,int depth){//n是第几层
if(root == NULL) return;
visit(root);
treeHeight = max(treeHeight,depth);
PreOrder(root->lchild,depth+1);
PrePrder(root->rchild,depth+1);
}
//中序遍历
void InOrder(BiTree root){
if(root == NULL) return;
InOrder(root->lchild);
visit(root);
Inorder(root->rchild);
}
//后序遍历
int PostOrder(BiTree root){
if(root == NULL) return;
int l = PostOrder(root->lchild);
int r = PostPrder(root->rchild);
cout<<"该树的平衡因子为"<<l-r<<endl;
visit(root);
}
//层序遍历,由于要实现Q,大题代码一般不考,如果考了,直接用队列
void InitQueue(Queue &Q);
void EnQueue(Queue &Q,BiTNode *x);
void DeQueue(Queue &Q,BitNode *x);
bool IsEmpty(Queue &Q);
void levelOrder(BiTree T){
Queue Q:
InitQueue(Q);
Bitree p;
Enqueue(Q.T);
while(!IsEmpty(Q)){
Dequeue(Q,p);//让队头元素出列
visit(p);//出队访问
if(p->left !=NULL)
EnQueue(Q,p->lchild);//
if(p->right !=NULL)
EnQueue(Q,p->right);
}
}
}
//求树的高度1(通过参数传递向下传递)
int TreeHeight = 0;
PreOrder(BiTree root ,int height){
if(root ==NULL) return;
visit(root);
if(height >TreeHeight)
TreeHeight = height;
PreOrder(root->left,height+1);
PreOrder(root->right,height+1);
}
PreeOrder(root,1)
//求树的高度2(通过返回值)
PostOrder(BiTree root){
if(root ==NULL) return;
int l = PostOrder(root->left,height+1);
int r = PostOrder(root->right,height+1);
TreeHeight = ( l > r ) ? l : r;
visit(root);
}
//求树的宽度:结点最多的那一层有多少结点
//最初想到的是层序,但是尽量用先中后
//利用先中后的参数或者返回值
//利用先序遍历作为代码的框架
int width[MAX];
void PreOrder(BiTree root,int level){//n是第几层
if(root == NULL) return;
width[level]++;
PreOrder(root->lchild,depth+1);
PrePrder(root->rchild,depth+1);
}
void treeWidth(BiTree T){
for(int i = 0;i<MAX;i++)
width[i] = 0;
PreOrder(root,0);
int maxWidth = 0;
for(int i = 0;i<MAX;i++){
if(width[i]>maxWidth)
maxWidth = width[i];
}
cout<<"树的宽度是:"<<maxWidth;g
}
//求WPL(带权路径长)
1,找到叶子结点,没有子节点
2,向下传参
int WPL;
void PreOrder(BiTree T,int n){
if(T==NULL) return;
if(T->left==NULL&&T->right==NULL){
WPL+=T->weight*n
}
PreOrder(root->lchild,depth+1);
PrePrder(root->rchild,depth+1);
}
PreOrder(root,0);
//判断二叉排序树
中序遍历,判断递增
int temp = MIN_INT;//已经访问过的最大值
bool isBST = True;
void InOrder(BiTree root){
if(root == NULL) return;
InOrder(root->lchild);
if(T->data >=temp) temp = T=>data;
else isBST = False;
Inorder(root->rchild);
}
//二叉树是否平衡
后续遍历返回树高
bool isBalance = true;
int PostOrder(BiTree root){
if(root == NULL) return;
int l = PostOrder(root->lchild);
int r = PostPrder(root->rchild);
if(left-right>1) isBalance = False;
if(left->righ<-1) isBalance = False;
if(left >right) return left+1;
else return right+1;
visit(root);
}
//完全二叉树
第一个不满的孩子是否是:有右但没左孩子
使用层序遍历
void InitQueue(Queue &Q);
void EnQueue(Queue &Q,BiTNode *x);
void DeQueue(Queue &Q,BitNode *x);
bool IsEmpty(Queue &Q);
void levelOrder(BiTree T){
Queue Q:
InitQueue(Q);
Bitree p;
Enqueue(Q.T);
while(!IsEmpty(Q)){
Dequeue(Q,p);//让队头元素出列
visit(p);//出队访问
if(p->left !=NULL)
EnQueue(Q,p->lchild);//
if(p->right !=NULL)
EnQueue(Q,p->right);
}
}
}
bool isComplete = true;//判断是否为完全二叉树
bool flag = false;//flag = true表示层序遍历时出现
//过叶子或者只有左孩子的分枝结点
void visit(BiTNode *p){
if(p->lchild==NULL&&p->rchild==NULL) flag = true;
//第一次访问到孩子缺失的结点
//如果是无左有右,可以判断一定不完全,
//如果是有左无右,此时还没没有办法判断,因为后续可能出现有问题的
//如果是无左无右,此时也没有办法判断,因为后续可能出现有问题的
//因此做个标记,表明后面不可以出现这两种情况,如果出现,那就是错的
if(p->lchild == NULL &&p->rchild!=NULL) isComplete = flase;
if(p->lchild!=NULL &&p->right==NULL{
if(flag) isComplete = false;
flag = true;
}
//前一步是为了判断这个结点是不是第二次出现
//flag = true是判断,这个结点,是不是第一次出现,如果是第一次出现,也即是flag是falsa,
//那就应当当做第一次出现来判断
if(p->lchild!=NULL &&p->right!=NULL{
if(flag) isComplete = false;
}
}