排序与排列

一、排序篇:Java中的sort函数

package practice;
import java.util.*;

public class Main {
	public static void main(String[] args) {
		int[] a= {7, 3, 6, 2, 4, 5, 9};
		Arrays.sort(a);//对数组进行排序
		for(int num:a) {
			System.out.print(num+" ");//升序
		}
		System.out.println();
		for(int i=a.length-1;i>=0;i--) {
			System.out.print(a[i]+" ");//降序
		}
	}

}

练习1:拼数

直接整数排序肯定不对。。。。

不能用单纯的字符串排序,这样不能达到最大,因为默认中前缀相同时,短<长

import java.util.Scanner;
import java.util.*;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n=scan.nextInt();
        String[] a=new String[n];
        for(int i=0;i<n;i++){
          a[i]=scan.next();//遇到空格就停,不能自己换行
        }
        Arrays.sort(a,(x,y)->{ return (x+y).compareTo(y+x);});//Arrays.sort是从小到大排序的,后面只是给了个比较的法则
        for(int i=n-1;i>=0;i--){//从大到小输出出来
          System.out.print(a[i]);
        }
        scan.close();
    }
}

或者将lamada公式换成冒泡排序:

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] nums = new int[n];
        for(int i = 0; i < n; i++)
            nums[i] = sc.nextInt();
        String[] s = new String[n];
        String ans = "";
        for(int i = 0; i < n; i++)
            s[i] = nums[i] + "";
        for(int i=1;i<=n-1;i++){
            for(int j=0;j<n-i;j++){
                if((s[j]+s[j+1]).compareTo(s[j+1]+s[j])<0){
                    String tmp=s[j];
                    s[j]=s[j+1];
                    s[j+1]=tmp;
                }
            }
        }
        for(int i = 0; i < n; i++) 
            ans += s[i];
        System.out.println(ans);
    }
}

练习二:错误票据

//package practice;
import java.util.*;
//1:无需package
//2: 类名必须Main, 不可修改

public class Main {
 public static void main(String[] args) {
     Scanner scan = new Scanner(System.in);
     ArrayList<Integer> list=new ArrayList<Integer>();
     int n=scan.nextInt();
     scan.nextLine();
     for(int i=0;i<n;i++) {
    	 String s=scan.nextLine();
    	 String[] sarr=s.split(" ");
    	 for(int j=0;j<sarr.length;j++) {
    		 list.add(Integer.valueOf(sarr[j]));
    	 }
     }
     Collections.sort(list);
     int duan=0,chong=0;
     for(int i=0;i<list.size()-1;i++){
       if((list.get(i+1)-list.get(i))==2){
         duan=list.get(i)+1;
       }else if(list.get(i).equals(list.get(i+1))){
         chong=list.get(i);
       }
     }
     System.out.println(duan+" "+chong);
     scan.close();
 }
}

练习三、奖学金

 

 主要是注意里面TreeSet排序算法的重写!!!!

package practice;
import java.util.*;
//1:无需package
//2: 类名必须Main, 不可修改

//重点在于重写比较器
public class Main {
 public static class Grade{
   //只关注有用的三个
   private int num;
   private int chinese;
   private int Grade;
   Grade(int num,int chinese,int Grade){//注意单词一定不能拼错
       this.num=num;
       this.chinese=chinese;
       this.Grade=Grade;
   }
 }
 public static void main(String[] args) {
     Scanner scan = new Scanner(System.in);
     int n=scan.nextInt();
     scan.nextLine();//必须的
     Set<Grade> set=new TreeSet<>(new Comparator<Grade>(){
    	 public int compare(Grade a,Grade b){
    		 if(a.Grade!=b.Grade){
    			 return b.Grade-a.Grade;
    		 }else if(a.chinese!=b.chinese){
    			 return b.chinese-a.chinese;
    		 }else{
    			 return a.num-b.num;//注意有的地方不是倒序!!!!!
    		 }
    	 }
     	});
     for(int i=1;i<=n;i++){
         String[] sarr=scan.nextLine().split(" ");
         int chinese=Integer.valueOf(sarr[0]);
         int sum=Integer.valueOf(sarr[0])+Integer.valueOf(sarr[1])+Integer.valueOf(sarr[2]);
         set.add(new Grade(i,chinese,sum));
       }
     int cot=0;
     for(Grade grade:set){
       System.out.println(grade.num+" "+grade.Grade);
       cot++;
       if(cot==5){
         break;
       }
     }

     scan.close();
 }
}

练习四:外卖店优先级

 注意Comparable类的继承,以及compareTo方法的改写

注意处理方式,先考虑输入的订单列表,然后再考虑剩下的

import java.util.*;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    public static class My implements Comparable<My>{
        int t,id;
        My(int t,int id){
            this.t=t;
            this.id=id;
        }
        public int compareTo(My m){
            if(t==m.t){
                if(id>m.id){
                    return 1;
                }else if(id<m.id){
                    return -1;
                }else{
                    return 0;
                }
            }
            return t > m.t ? 1 : -1;
        }
    }
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n=scan.nextInt();
        int m=scan.nextInt();
        int T=scan.nextInt();
        int[] s=new int[n+1];//存放每个店铺当前的优先值
        int[] pre=new int[n+1];//存放该id上一次有订单的时间
        boolean[] st=new boolean[n+1];
        My[] me =new My[m];
        for(int i=0;i<m;i++){
            int t=scan.nextInt();
            int id=scan.nextInt();
            me[i]=new My(t,id);
        }
        Arrays.sort(me);
        for(int i=0;i<m;){
            int j=i;
            //同一小时内重复送往一家的情况,为了计数
            while(j<m&&me[i].t==me[j].t&&me[i].id==me[j].id){
                j++;
            }
            int id=me[i].id,t=me[i].t,cnt=j-i;//cnt表示这一小时送的订单数
            i=j;
            s[id] -= t-pre[id]-1;//-1是因为最近的这一小时是有订单的
            if(s[id]<0) s[id]=0;
            if(s[id]<=3) st[id]=false;
            //注意这里的顺序,因为在等于零的情况下是不用减的,如果这个放前面则少算
            s[id]+=cnt*2;
            if(s[id]>5) st[id]=true;
            pre[id]=t;//存放这次出现订单的时间
        }
        for(int i=1;i<=n;i++){
            if(pre[i]<T){
                s[i] -=T-pre[i];
                if(s[i]<=3) st[i]=false;//因为这个循环中都是减少的,不会有超过5的
            }
        }
        int res=0;
        for(int i=1;i<=n;i++){
            if(st[i]){
                res++;
            }
        }
        scan.close();
        System.out.println(res);
    }
}

二、排列篇

全排列,java中没有提供函数,只能手写555

方法一:递归:

下面的排列依然用的是递归的思想

递归需要满足两个条件:

        1. 该问题可以转化为更小的子问题,且两者解决办法相同。

        2. 有出口,该出口通常就是最小的子问题(不可再分)的解决办法。

故该问题中,若要对n个子母进行全排列,可以看作将将某一个子母放在第一位固定,对后面的n-1个字母再进行全排列,满足第一个条件。在只有一个字母的时候可以直接输出,这是最小的问题,也是出口,满足第二个条件。

package practice;
import java.util.*;

public class Main {
	public static void permutation(char[] s,int from,int to) {
		if(to<=1) {
			return;
		}
		if(from==to) {
			System.out.println(s);
		}else {
			//用到递归思想
			for(int i=from;i<=to;i++) {
				swap(s,i,from);
				permutation(s,from+1,to);
				swap(s,from,i);//将前缀撤回,继续做上一个全追的排序
			}
		}
	}
	
	public static void swap(char[] s,int i,int j) {
		char tmp=s[i];
		s[i]=s[j];
		s[j]=tmp;
	}
	

	public static void main(String[] args) {
		char[] s= {'a','b','c'};
		permutation(s,0,2);  //permutation(数组,头,尾)  不包括尾
	}
}

方法二:字典序

如果当前排列是124653,找它的下一个排列的方法是,从这个序列中从右至左找第一个左邻小于右邻的数。如果找不到,则所有排列求解完成,如果找到则说明排列未完成。

本例中将找到46,计4所在的位置为i,找到后不能直接将4与6对换,而是要从右向左找第一个比4大的数,本例找到的是5,将其位置记为j,将i与j的位置交换,得到125643,然后将第i+1到最后一个元素从小到大排序的到125346,这就是124653的下一个排列:

public static boolean nextPermution(char[] data) {
	 int end=data.length-1;//从最右面开始
	 int swapPoint1=end,swapPoint2=end;
	 while(swapPoint1>0&&data[swapPoint1]<=data[swapPoint1]-1) {
		 swapPoint1--;
	 }
	 if(swapPoint1==0) {
		 return false;//已经没有后序了
	 }else {
		 //要替换的定位在swapPoint1-1:第一个不满足升序的
		 while(swapPoint2>0 && data[swapPoint2]<=data[swapPoint1-1]) {
			 swapPoint2--;
		 }
		 swap(data,swapPoint1-1,swapPoint2);
		 reverse(data,swapPoint1,end);//翻转,使后面几位的值最小
		 return true;
	 }
 }
 
 public static void swap(char[] data,int left,int right) {
	 char temp=data[left];
	 data[left]=data[right];
	 data[right]=temp;
 }
 
 public static void reverse(char[] data,int left,int right) {
	 for(int i=left,j=right;i<j;i++,j--) {
		 swap(data,i,j);
	 }
 }

例题1: 

【pk复试上机】

首先是常规的递归,将问题分解为第一个数和后面n-1个数,解决n个数的排序就是解决其后面n-1个数的排序问题。如果只剩下最后一个数则直接输出,即递归出口。 另外题目要求按照从小多大的顺序输出,则需要借助TreeMap进行排序,在JAVA中HashMap只有去重功能,不能排序。

import java.util.*;
import java.util.TreeMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Main{
    static Map<String,Integer> map=new TreeMap<String,Integer>();//进行排序
    public static void permutation(char[] s,int from,int to){
        if(to<1){
            System.out.println(s);
            return;
        }
         if(from==to){//只剩下一个的情况
            String S=String.copyValueOf(s);
            map.put(S,1);
        }else{
            for(int i=from;i<=to;i++){
                swap(s,from,i);
                permutation(s,from+1,to); //递归进行后面数字的排列
                swap(s,from,i);//每一层都换回来,保证顺序不会被打乱
            }
        }
    }
    
    public static void swap(char[] s,int a,int b){
        char tmp=s[a];
        s[a]=s[b];
        s[b]=tmp;
    }
    
    public static void main(String[] args){
        Scanner in=new Scanner(System.in);
        char[] s=in.nextLine().toCharArray();
        permutation(s,0,s.length-1);
        for(Entry<String,Integer> entry: map.entrySet()){
            System.out.println(entry.getKey());
        }
    }
}

练习2:外星人

我的代码,非常繁琐。。。。超时了:

package practice;
import java.util.*;
//1:无需package
//2: 类名必须Main, 不可修改

public class Main {
 public static int M=0;
 public static Map<String,Integer> m=new TreeMap<String,Integer>(); 
 public static String s;
 public static boolean exit=false;
 public static void main(String[] args) {
     Scanner scan = new Scanner(System.in);
     int N=scan.nextInt();
     M=scan.nextInt();
     scan.nextLine();
     String[] a=scan.nextLine().split(" ");
     StringBuffer sb2=new StringBuffer();
     for(int i=0;i<a.length;i++) {
    	 sb2.append(a[i]);
     }
     s=sb2.toString();
     permution(a,0,a.length-1);
     String ans="";
     for(String c:m.keySet()) {
    	 if(exit) {
    		 M--;
    		 if(M==0) {
    			 ans=c;
                 break;
    		 }
    	 }
    	 if(c.equals(s)) {
    		 exit=true;
    	 }
     }
     char[] C=ans.toCharArray();
     for(int i=0;i<C.length;i++){
       System.out.print(C[i]+" ");
     }
     scan.close();
 }
 public static void permution(String[] a,int from,int to) {
	 if(from==to) {
		 StringBuffer sb=new StringBuffer();
		 for(int i=0;i<a.length;i++) {
			 sb.append(a[i]);
		 }
		 String tmp=sb.toString();
		 m.put(tmp,1);
		 return;//注意返回
	 }
	 for(int i=0;i<a.length;i++) {
		 swap(a,from,i);
		 permution(a,from+1,to);
		 swap(a,from,i); //再换回来 防止重复
	 }
	 return;
 }
 public static void swap(String[] a,int i,int j) {
	 String tmp=a[i];
	 a[i]=a[j];
	 a[j]=tmp;
	 
 }
}

换用字典序的方法吧,碰上全排列java真的太卑微了呜呜呜~:

下面方法不超时,单说结果有误

package practice;
import java.util.*;
//1:无需package
//2: 类名必须Main, 不可修改

public class Main {
 public static void main(String[] args) {
	 Scanner scan = new Scanner(System.in);
     int N=scan.nextInt();
     int M=scan.nextInt();
     scan.nextLine();
     String[] a=scan.nextLine().split(" ");
     while(nextPermution(a)) {
    	 M--;
    	 if(M==0) {
    		 break;
    	 }
     }
     for(int i=0;i<a.length;i++) {
    	 System.out.print(a[i]+" ");
     }
     scan.close();
 }
 
 public static boolean nextPermution(String[] data) {
	 int end=data.length-1;//从最右面开始
	 int swapPoint1=end,swapPoint2=end;
	 while(swapPoint1>0&&data[swapPoint1].compareTo(data[swapPoint1-1])<0) {
		 swapPoint1--;
	 }
	 if(swapPoint1==0) {
		 return false;//已经没有后序了
	 }else {
		 //要替换的定位在swapPoint1-1:第一个不满足升序的
		 while(swapPoint2>0 && data[swapPoint2].compareTo(data[swapPoint1-1])<0) {
			 swapPoint2--;
		 }
		 swap(data,swapPoint1-1,swapPoint2);
		 reverse(data,swapPoint1,end);//翻转,使后面几位的值最小
	 }
	 return true;
 }
 
 public static void swap(String[] data,int left,int right) {
	 String temp=data[left];
	 data[left]=data[right];
	 data[right]=temp;
 }
 
 public static void reverse(String[] data,int left,int right) {
	 for(int i=left,j=right;i<j;i++,j--) {
		 swap(data,i,j);
	 }
 }
}

练习三:排列序数

package practice;
import java.util.*;
//1:无需package
//2: 类名必须Main, 不可修改

public class Main {
public static void main(String[] args) {
   Scanner scan = new Scanner(System.in);
   char[] in=scan.nextLine().toCharArray();//细心没有空格  不能用空格来分
   char[] temp=new char[in.length];
   for(int i=0;i<in.length;i++) {
	   temp[i]=in[i];
   }
   Arrays.sort(temp);
   int cnt=0;
   boolean ok=true;
   do {
	   for(int i=0;i<in.length;i++) {
		   if(temp[i]!=in[i]) {
			   ok=false;
			   break;
		   }
	   }
	   if(ok) {
		   System.out.println(cnt);
		   return;
	   }else {
		   ok=true;
	   }
	   cnt++;
   }while(nextPermution(temp));
   scan.close();
}

public static boolean nextPermution(char[] data) {
	 int end=data.length-1;
	 int swap1=end,swap2=end;
	 while(swap1>0 && data[swap1]<data[swap1-1]) {
		 swap1--;
	 }
	 if(swap1==0) {
		 return false; //已经是最大的 没有下一个
	 }else {
		while(swap2>0 && data[swap2]<data[swap1-1]) {
			swap2--;
		}
		swap(data,swap2,swap1-1);
		reverse(data,swap1,end); //翻转 保证后面变动的范围值最小
	 }
	 return true;
	 
}

public static void swap(char[] data,int left,int right) {
	 char temp=data[left];
	 data[left]=data[right];
	 data[right]=temp;
}

public static void reverse(char[] data,int left,int right) {
	 for(int i=left,j=right;i<j;i++,j--) {
		 swap(data,i,j);
	 }
}
}

练习四:带分数

package practice;
import java.util.*;
//1:无需package
//2: 类名必须Main, 不可修改

//带分数
//从最小的进行全排列
//然后进行划分,但是第一个最多只有三位,需要比较一下和100的大小
//剩下的从中间位置往后,知道相除的整数  再看加起来得不得100
public class Main {
public static void main(String[] args) {
   Scanner scan = new Scanner(System.in);
   int sum=scan.nextInt();
   int[] data=new int[9];
   for(int i=1;i<10;i++) {
	   data[i-1]=i;
   }
   int ans=0;
   do {
	   for(int i=0;i<7;i++) {
		   int rest=sum-toData(data,0,i); //去掉第一个数之后剩下的
		   if(rest<=0) {
			   break;//已经不能再增加了
		   }
		   for(int j=(i+1+8)/2;j<8;j++) {
			   if(toData(data,i+1,j)%toData(data,j+1,8)==0) {
				   if(toData(data,i+1,j)/toData(data,j+1,8)==rest) {
					   ans++;
					   break;//这种情况下只有一个对应的答案,所以找到之后可以直接break出内循环
				   }
			   }
		   }
	   }
   }while(nextPermution(data));
   System.out.println(ans);
   scan.close();
}

public static boolean nextPermution(int[] data) {
	 int end=data.length-1;
	 int swap1=end,swap2=end;
	 while(swap1>0 && data[swap1]<data[swap1-1]) {
		 swap1--;
	 }
	 if(swap1==0) {
		 return false; //已经是最大的 没有下一个
	 }else {
		while(swap2>0 && data[swap2]<data[swap1-1]) {
			swap2--;
		}
		swap(data,swap2,swap1-1);
		reverse(data,swap1,end); //翻转 保证后面变动的范围值最小
	 }
	 return true;
	 
}

public static void swap(int[] data,int left,int right) {
	 int temp=data[left];
	 data[left]=data[right];
	 data[right]=temp;
}

public static void reverse(int[] data,int left,int right) {
	 for(int i=left,j=right;i<j;i++,j--) {
		 swap(data,i,j);
	 }
}

public static int toData(int[] data,int begin,int end) {
	int ans=0;
	int n=1;
	if(end==begin) {
		return data[begin];
	}
	for(int i=end;i>=begin;i--) {
		ans+=n*data[i];
		n=n*10;
	}
	return ans;
}
}

练习五:第几个幸运数

方法一:暴力解法,直接循环,注意有一个零的值,所以范围不能等于59084709587505

# 用python直接暴力
import os
import sys

# 请在此输入您的代码
cnt=0
for i in range(50):
  for j in range(50):
    for k in range(50):
      a=3**i
      b=5**j
      c=7**k
      if a*b*c < 59084709587505: #不等于是因为多出来一个零的情况
        cnt=cnt+1
print(cnt)

//java暴力,注意大数使用long

import java.util.*;

//第几个幸运数字
public class Main {
public static void main(String[] args) {
   int cnt=0;
   long n=59084709587505L;//记得在末尾加上L
   for(int i=0;Math.pow(3,i)<59084709587505L;i++) {
	   for(int j=0;Math.pow(5, j)<59084709587505L;j++) {
		   for(int k=0;Math.pow(7, k)<59084709587505L;k++) {
			   if(Math.pow(3,i)*Math.pow(5, j)*Math.pow(7, k)<59084709587505L)
				   cnt++;
		   }
	   }
   }
   System.out.println(cnt);
}

方法二:优先队列+set去重

优先队列:PriorityQueue<Long> q=new PriorityQueue<Long>(); 总是返回队列中的最小值,实现原理是小顶堆

用Hashset进行去重,用优先队列保证每次取出的都是最小的,然后依次乘上3、5、7符合要求的话就放在优先队列和set里面,要注意去重

package practice;
import java.util.PriorityQueue;
import java.util.*;

//优先队列+set去重
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        long n=59084709587505L;//注意加上L
        long t=n;
        //优先队列,总是返回有限队列中的最小值
        PriorityQueue<Long> q=new PriorityQueue<Long>();
        //去重
        Set<Long> st=new HashSet<Long>();
        //将3、5、7放在num数组中
        long[] num={3,5,7};
        //加上最小的三个数
        for(int i=0;i<3;i++){
            q.add(num[i]);
            st.add(num[i]);
        }
        int cnt=0;
        //等于说从小到大的找,减少循环的次数
        while(q.isEmpty()==false){
            long h=q.poll();//返回最小的元素
            ++cnt;          //出来一个就加上
            if(h==n)  break; //已经到了n
            for(int i=0;i<3;i++){
                t=h*num[i];
                if(t>n) continue; //如果大于的话就略过,保证都是小雨的
                if(st.contains(t)==false){
                    q.add(t);
                    st.add(t);
                }

            }
        }
        System.out.println(cnt);
        scan.close();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值